# **********************************************************
# Copyright (c) 2010-2020 Google, Inc.  All rights reserved.
# Copyright (c) 2009-2010 VMware, Inc.  All rights reserved.
# **********************************************************

# Dr. Memory: the memory debugger
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License, and no later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

# DR requires 3.7 which means we must also.
cmake_minimum_required(VERSION 3.7)

include(make/policies.cmake NO_POLICY_SCOPE)

# like DR, we collapse VS generator into one config since
# we don't have enough control over output dirs
# in build rules (until cmake 2.8.4).
# this must be prior to the project() command.
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
  if ("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
    set(CMAKE_CONFIGURATION_TYPES "Debug" CACHE STRING "" FORCE)
  else ()
    set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo" CACHE STRING "" FORCE)
  endif ()
  # we want to use the _LOCATION_<config> property
  string(TOUPPER "${CMAKE_CONFIGURATION_TYPES}" upper)
  set(location_suffix "_${upper}")
else ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
  set(location_suffix "")
endif ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")

# I want to override the default CMAKE_INSTALL_PREFIX, but allow it to
# be set (as the same var name, so CPack and other standard tools
# work) externally.  The best solution is to check whether defined BEFORE
# the project() command.
# If we didn't use standard tools we could set CMAKE_INSTALL_PREFIX
# to be CACHE INTERNAL FORCE to INSTALL_PREFIX.
if (NOT DEFINED CMAKE_INSTALL_PREFIX)
  set(install_override ON)
else (NOT DEFINED CMAKE_INSTALL_PREFIX)
  set(install_override OFF)
endif (NOT DEFINED CMAKE_INSTALL_PREFIX)

project(DrMemory NONE)
if (DEFINED GENERATE_PDBS AND NOT GENERATE_PDBS)
  # support building over cygwin ssh where we cannot build pdbs.
  # using the same solution as DynamoRIO i#310.
  # To prevent cmake's try-compile for its working compiler test and
  # its ABI determination test we request a Release build config
  # via a custom Plaform/Windows-cl.cmake in our make/ dir.
  set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/make")
endif ()
enable_language(C)
enable_language(CXX)

include(CheckCCompilerFlag)

# The target OS:
if (APPLE)
  set(MACOS 1)
elseif (UNIX)
  set(LINUX 1)
endif (APPLE)

if (CMAKE_SYSTEM_NAME MATCHES "^Android")
  set(ANDROID 1)
  set(DEFINES ${DEFINES} -DANDROID)
  set(DRM_DEVICE_BASEDIR "/data/local/tmp" CACHE STRING "base dir for Android binaries")
  option(DRM_COPY_TO_DEVICE "copy cross-compiled binaries to DRM_DEVICE_BASEDIR" OFF)
  if (DRM_COPY_TO_DEVICE)
    find_program(ADB adb DOC "adb Android utility")
    if (NOT ADB)
      message(FATAL_ERROR "Unable to find adb for DRM_COPY_TO_DEVICE")
    else ()
      execute_process(COMMAND ${ADB} get-state
        RESULT_VARIABLE adb_result
        ERROR_VARIABLE adb_err
        OUTPUT_VARIABLE adb_out OUTPUT_STRIP_TRAILING_WHITESPACE)
      if (adb_result OR NOT adb_out STREQUAL "device")
        message(FATAL_ERROR "Android device not connected for DRM_COPY_TO_DEVICE")
      endif ()
      message(STATUS "Binaries will be copied to the attached Android device")
    endif ()
  endif ()
endif ()

# The target arch:
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
  set(ARM 1)
else ()
  set(X86 1)
endif ()

if (UNIX)
  set(DEFINES ${DEFINES} -DUNIX)
endif (UNIX)

option(VMKERNEL "target vmkernel")
if (VMKERNEL)
  # if we get enough of these, should use a configure.h, but would need to
  # pull the ops out like DR core does to pass to options_perl below
  set(DEFINES ${DEFINES} -DVMX86_SERVER)
endif (VMKERNEL)

option(TOOL_DR_HEAPSTAT "build Dr. Heapstat instead of Dr. Memory")
option(USE_MD5 "use md5 instead of crc32 for callstack hashes for Dr. Heapstat")
if (USE_MD5)
  set(DEFINES ${DEFINES} -DUSE_MD5)
endif (USE_MD5)
option(CHECK_WITH_MD5 "use crc32 for callstack hashes but check for collisions with md5")
if (CHECK_WITH_MD5)
  set(DEFINES ${DEFINES} -DCHECK_WITH_MD5)
endif (CHECK_WITH_MD5)
option(STATIC_DRSYMS "use static drsyms library" ON)
if (UNIX AND NOT STATIC_DRSYMS)
  # we could support dynamic but we'd have to copy libdrsym.so
  message(FATAL_ERROR "non-static drsyms not supported for Linux")
endif ()

option(TEST_SUITE "we are running a series of builds for official purposes")

if (TOOL_DR_HEAPSTAT)
  # Dr. Heapstat
  set(TOOL_DR_MEMORY OFF)
  set(toolname drheapstat)
  set(tooldir ${toolname})
  set(toolname_cap DrHeapstat)
  set(toolname_cap_spc "Dr. Heapstat")
  set(DEFINES ${DEFINES} -DTOOL_DR_HEAPSTAT)

  # Dr. Heapstat uses drsyms only to avoid false neg on leaks (i#762, i#292).
  # XXX: use drsyms for leak reports (i#926) and usage (i#282).
  set(DRSYMS_DEFAULT ON)
else (TOOL_DR_HEAPSTAT)
  # Dr. Memory
  set(TOOL_DR_MEMORY ON)
  set(toolname drmemory)
  set(tooldir ${toolname})
  set(toolname_cap DrMemory)
  set(toolname_cap_spc "Dr. Memory")
  set(DEFINES ${DEFINES} -DTOOL_DR_MEMORY)
  set(DRSYMS_DEFAULT ON)

endif (TOOL_DR_HEAPSTAT)

# We use a monotonically increasing integer that's larger than any bugfix
# release version as the patchlevel ver# to distinguish
# We used to use the svn revision (i#83) and we leave that code in place
# (for now at least) for anyone building an old checkout.
# For git, we follow DRi#1565 and use a date.
set(VERSION_NUMBER_PATCHLEVEL 0)
if (EXISTS "${PROJECT_SOURCE_DIR}/.svn")
  find_program(SVN svn DOC "subversion client")
  if (SVN)
    execute_process(COMMAND ${SVN} info
      WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
      RESULT_VARIABLE svn_result
      ERROR_VARIABLE svn_err
      OUTPUT_VARIABLE svn_out)
    if (svn_result OR svn_err)
      message(FATAL_ERROR "*** ${SVN} info failed: ***\n${svn_result} ${svn_err}")
    endif (svn_result OR svn_err)
    string(REGEX MATCH "Revision: [0-9]+" svn_out "${svn_out}")
    string(REGEX REPLACE "Revision: " "" svn_out "${svn_out}")
    set(VERSION_NUMBER_PATCHLEVEL "${svn_out}")
  endif (SVN)
else (EXISTS "${PROJECT_SOURCE_DIR}/.svn")
  if (EXISTS "${PROJECT_SOURCE_DIR}/.git")
    find_program(GIT git DOC "git client")
    if (GIT)
      # We want the committer date (not author date) (xref DRi#1565).  We request
      # UNIX timestamp format and then divide down to days to get a small enough
      # number for the Windows resource limits.
      execute_process(COMMAND ${GIT} log -n 1 --format=%ct
        WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
        RESULT_VARIABLE git_result
        ERROR_VARIABLE git_err
        OUTPUT_VARIABLE git_out)
      if (git_result OR git_err)
        message("*** ${GIT} log failed: ***\n${git_err}")
      else (git_result OR git_err)
        math(EXPR daycount "${git_out} / (60*60*24)")
      endif (git_result OR git_err)
    endif (GIT)
    if (NOT daycount)
      # XXX DRi#1565: to support building when not in a git repo (e.g., from a source
      # tarball) we use date to get current time for timestamp.
      # This is not ideal as it confuses the build timestamp with the commit
      # timestamp.  We should add support for a local file holding the version.
      find_program(DATE date DOC "system date")
      if (DATE)
        execute_process(COMMAND ${DATE} +%s
          RESULT_VARIABLE date_result
          ERROR_VARIABLE date_err
          OUTPUT_VARIABLE date_out)
        if (date_result OR date_err)
          message("*** ${DATE} failed: ***\n${date_err}")
        else (date_result OR date_err)
          math(EXPR daycount "${date_out} / (60*60*24)")
        endif (date_result OR date_err)
      endif (DATE)
    endif (NOT daycount)
    if (NOT daycount)
      # set a much further date in the future to avoid confusing
      # this fake date with the real date from git log
      set(daycount 33333)
    endif (NOT daycount)
    set(VERSION_NUMBER_PATCHLEVEL "${daycount}")
  endif (EXISTS "${PROJECT_SOURCE_DIR}/.git")
endif (EXISTS "${PROJECT_SOURCE_DIR}/.svn")

if (APPLE)
  # clang linker disallows any but major # being >= 256 (1024 for x64) so we do
  # mod 200 (we assume we'll never confuse versions 200 apart) and add 56 (to
  # distinguish from real releases).
  math(EXPR VERSION_NUMBER_PATCHLEVEL "(${VERSION_NUMBER_PATCHLEVEL} % 200) + 56")
endif (APPLE)

# N.B.: when updating this, update the git tag in .travis.yml.
# We should find a way to share (xref DRi#1565).
set(VERSION_NUMBER_DEFAULT "2.3.${VERSION_NUMBER_PATCHLEVEL}")
# Do not store the default TOOL_VERSION_NUMBER in the cache to prevent a stale one
# from preventing future version updates in a pre-existing build dir.
# Avoid "VERSION_NUMBER" name since conflics w/ DR's var.
set(TOOL_VERSION_NUMBER "" CACHE STRING "Version number: leave empty for default")
if ("${TOOL_VERSION_NUMBER}" STREQUAL "")
  set(TOOL_VERSION_NUMBER ${VERSION_NUMBER_DEFAULT})
endif()
message(STATUS "Dr. Memory version number: ${TOOL_VERSION_NUMBER}")

# Avoid "BUILD_NUMBER" name since conflics w/ DR's var
set(TOOL_BUILD_NUMBER "1" CACHE STRING "Build number (must be <64K)")
# However, we do want to set it to avoid "custom build" in messages
# from embedded DR when run alone (i#1717's dr_set_client_version_string() has
# already solved this for messages for DrMem on DR).  To make it easy to
# correlate, we set it to a formula computed from the DrMem ver.
string(REPLACE "." ";" ver_list ${TOOL_VERSION_NUMBER})
list(GET ver_list 0 TOOL_VERSION_MAJOR)
list(GET ver_list 1 TOOL_VERSION_MINOR)
math(EXPR BUILD_NUMBER
  "${TOOL_VERSION_MAJOR}*100 + ${TOOL_VERSION_MINOR}*10 + ${VERSION_NUMBER_PATCHLEVEL}")

string(REGEX REPLACE "\\." "," TOOL_VERSION_COMMAS "${TOOL_VERSION_NUMBER}")

set(DEFINES ${DEFINES}
  -DBUILD_NUMBER=${TOOL_BUILD_NUMBER}
  -DVERSION_NUMBER=${TOOL_VERSION_NUMBER}
  -DVERSION_STRING="${TOOL_VERSION_NUMBER}"
  -DVERSION_COMMAS=${TOOL_VERSION_COMMAS})

# i#44/PR 243532: online symbol access
option(USE_DRSYMS "use drsyms DR Extension online symbols instead of post-processing"
  ${DRSYMS_DEFAULT})
if (USE_DRSYMS)
  set(DEFINES ${DEFINES} -DUSE_DRSYMS)
endif (USE_DRSYMS)

if (WIN32)
  # With the new drinjectlib-based front end we do not use perl or perl2exe
  # at all on Windows (xref i#265/PR 486139)
  if (OFF) # disabling
    # For portability we convert our perl scripts to executables,
    # if the 'pp' tool is available.
    # For most portable results, use a native windows perl (e.g.,
    # strawberry perl) rather than cygwin perl.
    set(PERL_PATH  "" CACHE PATH "directory where perl binaries are located")
    if (PERL_PATH AND EXISTS "${PERL_PATH}/perl.exe")
      set(PERL_EXECUTABLE "${PERL_PATH}/perl.exe")
    else ()
      # we only look for perl if user doesn't specify (on windows it's common
      # to have several perls installed)
      include(FindPerl)
      if (PERL_FOUND)
        get_filename_component(PERL_PATH "${PERL_EXECUTABLE}" PATH)
      endif (PERL_FOUND)
    endif ()

    if (EXISTS "${PERL_PATH}/pp")
      set(PERL_PP "${PERL_PATH}/pp" CACHE FILEPATH
        "Path to perl PAR module's perl-to-executable tool 'pp'")
    else ()
      find_file(PERL_PP pp HINTS "${PERL_PATH}"
        DOC "Path to perl PAR module's perl-to-executable tool 'pp'")
    endif ()
    if (PERL_PP-NOTFOUND OR NOT EXISTS "${PERL_PP}")
      message(STATUS "Did not find pp: will not convert perl scripts to executables")
      set(PERL_TO_EXE OFF)
    else (PERL_PP-NOTFOUND OR NOT EXISTS "${PERL_PP}")
      message(STATUS "Found pp: ${PERL_PP}")
      set(PERL_TO_EXE ON)
    endif (PERL_PP-NOTFOUND OR NOT EXISTS "${PERL_PP}")
  else (OFF)
    set(PERL_TO_EXE OFF)
  endif (OFF)
endif (WIN32)

if (CMAKE_C_SIZEOF_DATA_PTR EQUAL 8 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
  set(X64 ON)
  set(LIB_ARCH "lib64")
  set(BIN_ARCH "bin64")
  set(DEFINES ${DEFINES} -DX64)
else()
  set(X64 OFF)
  set(LIB_ARCH "lib32")
  set(BIN_ARCH "bin32")
endif ()

if ("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
  # FIXME: use a configure.h
  set(DEFINES ${DEFINES} -DDEBUG -DSTATISTICS)
  set(DEBUG_BUILD ON) # "DEBUG" conflicts w/ DR
else ()
  # We want Windows pdb and Unix line #s for release build.
  # We make separate debug info for Unix, and are fine shipping it to users
  # in any case as we're open-source.
  set(CMAKE_BUILD_TYPE "RelWithDebInfo")
  set(DEBUG_BUILD OFF)
endif ()

string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER)

# i#1781: cmake 2.8.12+ fails to create static lib pdb by default
# XXX: we should share this with DR's copy
macro(add_static_lib_debug_info target dest_dir)
  if (WIN32)
    if ("${CMAKE_VERSION}" VERSION_EQUAL "3.1" OR
        "${CMAKE_VERSION}" VERSION_GREATER "3.1")
      append_property_string(TARGET ${target}
        COMPILE_PDB_NAME${location_suffix} "${target}"
        COMPILE_PDB_OUTPUT_DIRECTORY{location_suffix} "${dest_dir}")
    else ()
      # We just don't support it for < 3.1
    endif ()
  endif ()
endmacro()

if (UNIX)
  # there's no cmake warning control so we hardcode it
  # disabling strict aliasing since giving weird warning I'm not sure how to fix:
  #   alloc.c:716: warning: dereferencing type-punned pointer will break strict-aliasing rules
  set(WARN "-Wall -Werror -Wno-strict-aliasing")
  # Disabling format-truncation due to too many warnings about string construction
  # where truncating added strings is fine if the final doesn't fit anyway.
  CHECK_C_COMPILER_FLAG("-Wno-format-truncation" have_format_truncation_warning)
  if (have_format_truncation_warning)
    set(WARN "${WARN} -Wno-format-truncation")
  endif ()
  if (CMAKE_C_COMPILER MATCHES "/build/toolchain")
    # needed for linux/ipmi.h (PR 531644)
    set(EXTRA_FLAGS "-idirafter /build/toolchain/lin32/glibc-2007q3-51/usr/include")
  else ()
    if (APPLE AND NOT CMAKE_COMPILER_IS_GNUCC)
      # Ensure our binaries can run on older OSX
      set(EXTRA_FLAGS "-mmacosx-version-min=10.9")
    else ()
      set(EXTRA_FLAGS "")
    endif ()
  endif ()
  if (ARM)
    set(EXTRA_FLAGS "${EXTRA_FLAGS} -mthumb -march=armv7-a")
    if (ANDROID OR CMAKE_C_LIBRARY_ARCHITECTURE MATCHES "gnueabi$")
      set(EXTRA_FLAGS "${EXTRA_FLAGS} -mfloat-abi=softfp")
      # Android requires PIE.  We export symbols to match our test assumptions.
      set(CMAKE_EXE_LINKER_FLAGS
        "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie -Wl,--export-dynamic")
    endif ()
  endif ()
  # We use C++11.
  set(EXTRA_CXXFLAGS "-std=c++11")
  set(CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}
    "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}} ${ARCH_CFLAGS} ${WARN} ${EXTRA_FLAGS}")
  set(CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}
    "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}} ${ARCH_CFLAGS} ${WARN} ${EXTRA_FLAGS} ${EXTRA_CXXFLAGS}")
else (UNIX)
  # FIXME i#1204: fix warnings and up to /W4
  set(WARN "/W2 /WX")
  # update flags for our types and Debug (tests always use Debug).
  # ok to double-update b/c these are are regex-replace.
  foreach (config ${CMAKE_BUILD_TYPE} ${CMAKE_CONFIGURATION_TYPES} Debug)
    string(TOUPPER "${config}" config_upper)
    foreach (var CMAKE_C_FLAGS;CMAKE_CXX_FLAGS;
        CMAKE_C_FLAGS_${config_upper};
        CMAKE_CXX_FLAGS_${config_upper})
      # default from cmake has /W3 so remove to avoid warning about overriding
      string(REGEX REPLACE "/W[0-9]" "" ${var} "${${var}}")
      # /GZ requires RTC runtime support which we don't want (i#925)
      string(REGEX REPLACE "/GZ" "" ${var} "${${var}}")
      # avoid warnings (i#925)
      string(REGEX REPLACE "/GX" "/EHsc" ${var} "${${var}}")
      if (NOT DEBUG_BUILD)
        # RelWithDebInfo asks for /Ob1 but we want full inlining
        string(REGEX REPLACE "/Ob1" "/Ob2" ${var} "${${var}}")
      endif ()
    endforeach ()
  endforeach ()
  # disable stack protection: "unresolved external symbol ___security_cookie"
  set(CL_CFLAGS "/GS-")
  # build in parallel, always.
  # note that /MP is not officially supported on VS 2005 and others
  # have seen occasional problems: we'll risk it.  we could check for
  # "MSVC10 OR MSVC90".
  set(CL_CFLAGS "${CL_CFLAGS} /MP")
  if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 18.0)
    # i#1376: VS2013 requires /FS w/ multiple cl.exe in parallel (which Ninja
    # uses).  While /MP is supposed to enable it, it doesn't seem to.
    # This is recommended after /Fd but it seems to work here.
    set(CL_CFLAGS "${CL_CFLAGS} /FS")
  endif()
  set(CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER} "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}} ${WARN} ${CL_CFLAGS}")
endif (UNIX)
set(CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER} "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}} ${ARCH_CFLAGS} ${WARN}")
string(STRIP "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
  CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER})
string(STRIP "${CMAKE_CXX_FLAGS}" CMAKE_CXX_FLAGS)

if (WIN32)
  # DRi#1424: target the oldest possible platform we can
  if (X64)
    set(os_target "5.02") # Win2003x64/WinXPx64
  else (X64)
    if (CMAKE_C_COMPILER_VERSION VERSION_LESS 17.0) # up to and including VS2010
      set(os_target "5.00") # Win2K
    else () # VS2012, VS2013
      set(os_target "5.01") # WinXP
    endif ()
  endif (X64)
  foreach (lflags CMAKE_EXE_LINKER_FLAGS
      CMAKE_MODULE_LINKER_FLAGS
      CMAKE_SHARED_LINKER_FLAGS)
    set(${lflags} "${${lflags}} /subsystem:console,${os_target}")
  endforeach()
  message(STATUS "Targeting subsystem ${os_target}")
endif (WIN32)

if (UNIX)
  include(CheckIncludeFiles)
  check_include_files("asm-i386/stat.h" HAVE_ASM_I386)
  if (HAVE_ASM_I386)
    # see comments above about adding configure.h
    set(DEFINES ${DEFINES} -DHAVE_ASM_I386)
  endif (HAVE_ASM_I386)
endif (UNIX)

if (UNIX)
  # We don't want our instrument_init() pre-empted by debug-internal DynamoRIO
  # that has visible internal routines.
  CHECK_C_COMPILER_FLAG("-fvisibility=internal" HAVE_FVISIBILITY_INTERNAL)
  CHECK_C_COMPILER_FLAG("-fvisibility=hidden" HAVE_FVISIBILITY_HIDDEN)
  # Mac accepts internal but warns about it so we avoid it there.
  if (HAVE_FVISIBILITY_INTERNAL AND NOT APPLE)
    set(VISIBILITY "internal")
  elseif (HAVE_FVISIBILITY_HIDDEN)
    set(VISIBILITY "hidden")
  else ()
    message("${CMAKE_C_COMPILER} missing flag -fvisibility, using linker "
            "script instead")
    set(VISIBILITY " ")
  endif ()
  if (HAVE_FVISIBILITY_INTERNAL OR HAVE_FVISIBILITY_HIDDEN)
    set(CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}
      "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}} -fvisibility=${VISIBILITY}")
  endif ()

  if (APPLE)
    # Incredibly, for both clang and g++, while a single compile-and-link
    # invocation will create an executable.dSYM/ dir with debug info,
    # with separate compilation the final link does NOT create the
    # dSYM dir.
    # The "dsymutil" program will create the dSYM dir for us.
    # Strangely it takes in the executable and not the object
    # files even though it's the latter that contain the debug info.
    # Thus it will only work if the object files are still sitting around.
    find_program(DSYMUTIL_PROGRAM dsymutil)
    if (DSYMUTIL_PROGRAM)
      set(CMAKE_C_LINK_EXECUTABLE
        "${CMAKE_C_LINK_EXECUTABLE}"
        "${DSYMUTIL_PROGRAM} <TARGET>")
      set(CMAKE_C_CREATE_SHARED_LIBRARY
        "${CMAKE_C_CREATE_SHARED_LIBRARY}"
        "${DSYMUTIL_PROGRAM} <TARGET>")
      set(CMAKE_CXX_LINK_EXECUTABLE
        "${CMAKE_CXX_LINK_EXECUTABLE}"
        "${DSYMUTIL_PROGRAM} <TARGET>")
      set(CMAKE_CXX_CREATE_SHARED_LIBRARY
        "${CMAKE_CXX_CREATE_SHARED_LIBRARY}"
        "${DSYMUTIL_PROGRAM} <TARGET>")
    endif ()
  endif ()
endif (UNIX)

if (WIN32)
  # Use convention of DynamoRIO sources: DDKROOT env var (or DDK_ROOT cmake var).
  # We don't require the DDK as dbghelp and symsrv are in the SDK as well, and
  # we get ntdll_imports.lib from DR.
  set(DDK_ROOT "$ENV{DDKROOT}" CACHE PATH "Path to DDK or WDK.")
  if ("${DDK_ROOT}" STREQUAL "")
    # Check default install path
    if (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/3790.1830/")
      set(DDK_ROOT "$ENV{SYSTEMDRIVE}/WINDDK/3790.1830/")
    elseif (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/6000/")
      set(DDK_ROOT "$ENV{SYSTEMDRIVE}/WINDDK/6000/")
    elseif (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/7600.16385.1/")
      set(DDK_ROOT "$ENV{SYSTEMDRIVE}/WINDDK/7600.16385.1/")
    endif ()
  endif ("${DDK_ROOT}" STREQUAL "")

  # We can't include directly from DDK b/c the DDK include dir and VS include
  # dirs are incompatible, so we have our own copies of the headers we need.
  include_directories(wininc/psdk wininc/dxsdk)

  # We need a newer version of dbghelp.dll than is in system32/ on 2K or XP.
  # The versions that come with Debugging Tools for Windows are redistributable:
  # "you can distribute the DLL with your application".
  # The dbghelp.dll that comes in system32/ is not redistributable.
  # We want 6.3+ for full drsyms features.
  # 5.2 does not work (it's not just slower w/o SymSearch: it fails).
  # Haven't tested in between.
  # WINDDK/3790.1830/bin/x86/dbghelp.dll is 6.3.
  # Older CMake binaries are 32-bit but newer ones can be 64-bit so we
  # cannot rely on one or the other.
  if ("$ENV{PROGRAMW6432}" STREQUAL "")
    if (X64)
      message(FATAL_ERROR "On 32-bit Windows: 64-bit build not supported")
    endif ()
    set(PROGFILES "$ENV{PROGRAMFILES}")
    set(PROGFILES32 "$ENV{PROGRAMFILES}")
    set(ARCH_SFX "x86")
    set(DDK_SFX "i386")
  else ()
    set(PROGFILES "$ENV{PROGRAMW6432}")
    set(PROGFILES32 "$ENV{PROGRAMFILES\(x86\)}")
    if ("${PROGFILES32}" STREQUAL "")
      set(PROGFILES32 "$ENV{PROGRAMFILES}")
    endif ()
    if (X64)
      set(ARCH_SFX "x64")
      set(DDK_SFX "amd64")
    else (X64)
      set(ARCH_SFX "x86")
      set(DDK_SFX "i386")
    endif (X64)
  endif ()
  # Allow packaging to specify a glob for a prefered path for x86 and x64.
  set(DBGHELP_GLOB "" CACHE STRING
    "Preferred path for dbghelp.dll with wildcard expansion")
  # Even the VS2005 copy is 6.5 (despite its headers being < 6.3) so we can
  # use those as well as the later SDK and standalone DTFW versions.
  set(dbghelp_paths
    "${DBGHELP_GLOB}"
    "${DDK_ROOT}/bin/${ARCH_SFX}/dbghelp.dll"
    "${DDK_ROOT}/tools/tracing/${DDK_SFX}/dbghelp.dll"
    "${PROGFILES32}/Windows Kits/*/Debuggers/${ARCH_SFX}/dbghelp.dll"
    # In case if SDK is not installed and we have Visual Studio, dbghelp.dll may be found in the
    # Visual Studio's directory in subfolders Remote Debugger/x86 or Remote Debugger/x64 (xref i#1956).
    # We look in both paths (Program Files and Program Files (x86)) because some versions of
    # Visual Studio put dbghelp.dll in progfiles (especially 2008) and some in progfiles32.
    "${PROGFILES}/Microsoft Visual Studio */Common7/IDE/Remote Debugger/${ARCH_SFX}/dbghelp.dll"
    "${PROGFILES32}/Microsoft Visual Studio */Common7/IDE/Remote Debugger/${ARCH_SFX}/dbghelp.dll"
    "${PROGFILES32}/Microsoft Visual Studio */Common7/Packages/Debugger/${ARCH_SFX}/dbghelp.dll"
    "${PROGFILES32}/Microsoft Visual Studio/*/Professional/Common7/IDE/Remote Debugger/${ARCH_SFX}/dbghelp.dll")

  if (X64)
    set(dbghelp_paths ${dbghelp_paths}
      # For older versions of windbg, x64 dbghelp.dll resides here.
      "${PROGFILES}/Debugging Tools for Windows (x64)/dbghelp.dll")
  else (X64)
    set(dbghelp_paths ${dbghelp_paths}
      "${PROGFILES}/Microsoft Visual Studio */Common7/IDE/dbghelp.dll"
      # Putting this last mainly b/c only older versions (like my 6.3) are here.
      "${PROGFILES}/Debugging Tools for Windows/dbghelp.dll")
  endif (X64)
  file(GLOB dbghelp_hint ${dbghelp_paths})
  # XXX i#908: it seems cmake has trouble to lookup dbghelp.dll in
  # 64-bit directory, so we just explicitly check several possible locations.
  # Plus, we don't want system32, but NO_SYSTEM_ENVIRONMENT_PATH also
  # excludes Program Files, so we avoid find_file() in general.
  if (dbghelp_hint)
    # DRi#1219: exclude VS2005 x64 dbghelp as it is buggy
    list(LENGTH dbghelp_hint dbghelp_max)
    math(EXPR dbghelp_max "${dbghelp_max} - 1")
    set(dbghelp_index 0)
    list(GET dbghelp_hint 0 dbghelp_default)
    while (${dbghelp_index} LESS ${dbghelp_max})
      if (X64 AND dbghelp_default MATCHES "Visual Studio 8")
        # Keep looking.
      elseif (X64 AND dbghelp_default MATCHES "/x86/")
        # Keep looking.
      elseif (NOT X64 AND dbghelp_default MATCHES "/x64/")
        # Keep looking.
      elseif (NOT X64 AND dbghelp_default MATCHES "/amd64/")
        # Keep looking.
      else ()
        break ()
      endif ()
      math(EXPR dbghelp_index "${dbghelp_index} + 1")
      list(GET dbghelp_hint ${dbghelp_index} dbghelp_default)
    endwhile()
    if (X64 AND dbghelp_default MATCHES "Visual Studio 8")
      message(STATUS "Unable to find non-VS2005 dbghelp.dll")
      set(dbghelp_default "DBGHELP_DLL-NOTFOUND")
    endif ()
  else ()
    set(dbghelp_default "DBGHELP_DLL-NOTFOUND")
  endif ()
  set(DBGHELP_DLL "" CACHE STRING
    "location of dbghelp.dll from recent Debugging Tools for Windows")
  if ("${DBGHELP_DLL}" STREQUAL "")
    set(DBGHELP_DLL ${dbghelp_default})
  endif()
  if (DBGHELP_DLL-NOTFOUND OR NOT EXISTS "${DBGHELP_DLL}")
    message(FATAL_ERROR "dbghelp.dll required and not found")
  else ()
    message(STATUS "Using ${DBGHELP_DLL}")
  endif ()
  # Allow packaging to specify a glob for a prefered path for x86 and x64.
  set(SYMSRV_GLOB "" CACHE STRING
    "Preferred path for dbghelp.dll with wildcard expansion")
  set(symsrv_paths
    "${SYMSRV_GLOB}"
    "${PROGFILES32}/Windows Kits/*/Debuggers/${ARCH_SFX}/symsrv.dll"
    # In case if SDK is not installed and we have Visual Studio, symsrv.dll may be found in the
    # same place as dbghelp.dll (see comment for dbghelp.dll) (xref i#1956).
    "${PROGFILES}/Microsoft Visual Studio */Common7/IDE/Remote Debugger/${ARCH_SFX}/symsrv.dll"
    "${PROGFILES32}/Microsoft Visual Studio */Common7/IDE/Remote Debugger/${ARCH_SFX}/symsrv.dll"
    "${PROGFILES32}/Microsoft Visual Studio */Common7/Packages/Debugger/${ARCH_SFX}/symsrv.dll")

  if (X64)
    set(symsrv_paths ${symsrv_paths}
      # For older versions of windbg, x64 symsrv.dll resides here.
      "${PROGFILES}/Debugging Tools for Windows (x64)/symsrv.dll")
  else (X64)
    set(symsrv_paths ${symsrv_paths}
      "${PROGFILES}/Microsoft Visual Studio */Common7/IDE/symsrv.dll"
      # Putting this last mainly b/c only older versions (like my 6.3) are here.
      "${PROGFILES}/Debugging Tools for Windows/symsrv.dll")
  endif (X64)
  file(GLOB symsrv_hint ${symsrv_paths})
  if (symsrv_hint)
    list(GET symsrv_hint 0 symsrv_default)
    list(LENGTH symsrv_hint symsrv_max)
    math(EXPR symsrv_max "${symsrv_max} - 1")
    set(symsrv_index 0)
    while (${symsrv_index} LESS ${symsrv_max})
      if (X64 AND symsrv_default MATCHES "/x86/")
        # Keep looking.
      elseif (NOT X64 AND symsrv_default MATCHES "/x64/")
        # Keep looking.
      elseif (NOT X64 AND symsrv_default MATCHES "/amd64/")
        # Keep looking.
      else ()
        break ()
      endif ()
      math(EXPR symsrv_index "${symsrv_index} + 1")
      list(GET symsrv_hint ${symsrv_index} symsrv_default)
    endwhile()
  else ()
    set(symsrv_default "SYMSRV_DLL-NOTFOUND")
  endif ()
  set(SYMSRV_DLL "" CACHE STRING
    "location of symsrv.dll from recent Debugging Tools for Windows")
  if ("${SYMSRV_DLL}" STREQUAL "")
    set(SYMSRV_DLL ${symsrv_default})
  endif()
  if (SYMSRV_DLL-NOTFOUND OR NOT EXISTS "${SYMSRV_DLL}")
    message(FATAL_ERROR "symsrv.dll required and not found")
  else ()
    message(STATUS "Using ${SYMSRV_DLL}")
  endif ()

endif (WIN32)

if (BUILDING_SUB_PACKAGE)
  set(INSTALL_PREFIX "drmemory/")
else ()
  set(INSTALL_PREFIX "")
endif()

# To run out of build dir we put libs and scripts in dirs that match install layout
# except minus the toolname prefix dir.
# The CPack NSIS interface requires a bin/ dir, so for the Windows package
# we prefix bin/
# XXX: should clean all this up and normalize across platforms now
# that we have drsyms on Linux and once we're sure we don't need to
# support the old layout.
if (WIN32 AND USE_DRSYMS AND TOOL_DR_MEMORY)
  set(INSTALL_BIN_PREFIX "${INSTALL_PREFIX}.")
  set(BUILD_BIN_PREFIX ".")
elseif (PERL_TO_EXE)
  # We are only releasing Dr. Memory so no toolnames: would need to create
  # shortcut or .bat file to run from right dir
  if (X64)
    set(INSTALL_BIN_PREFIX "${INSTALL_PREFIX}bin64")
    set(BUILD_BIN_PREFIX "bin64")
  else (X64)
    set(INSTALL_BIN_PREFIX "${INSTALL_PREFIX}bin")
    set(BUILD_BIN_PREFIX "bin")
  endif (X64)
else (PERL_TO_EXE)
  # Unified layout: match Windows since we're moving toward C-based frontends
  # that won't have auxiliary scripts we want to hide.
  set(INSTALL_BIN_PREFIX "${INSTALL_PREFIX}.")
  set(BUILD_BIN_PREFIX ".")
endif (WIN32 AND USE_DRSYMS AND TOOL_DR_MEMORY)
# For NSIS we have everything in top-level bin/
# Once we have x64 we'll need to address: fix CPack?
if (X64)
  set(INSTALL_BIN "${INSTALL_BIN_PREFIX}/bin64")
  set(BUILD_BIN "${BUILD_BIN_PREFIX}/bin64")
else (X64)
  set(INSTALL_BIN "${INSTALL_BIN_PREFIX}/bin")
  set(BUILD_BIN "${BUILD_BIN_PREFIX}/bin")
endif (X64)
if (DEBUG_BUILD)
  set(build_type "debug")
else (DEBUG_BUILD)
  set(build_type "release")
endif (DEBUG_BUILD)
set(INSTALL_LIB "${INSTALL_BIN}/${build_type}")
set(BUILD_LIB "${BUILD_BIN}/${build_type}")

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${BUILD_LIB}")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${BUILD_BIN}")
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
  # we don't support the Debug and Release subdirs
  foreach (config ${CMAKE_CONFIGURATION_TYPES})
    string(TOUPPER "${config}" config_upper)
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config_upper}
      "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config_upper}
      "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}")
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config_upper}
      "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
  endforeach ()
endif ()

##################################################
# option sharing (PR 478146)

set(CPP_IGNORE_EMPTY "")
if (UNIX)
  if (APPLE)
    # /usr/bin/cpp is broken and won't honor -B flags
    set(CMAKE_CPP_FOR_OPS ${CMAKE_C_COMPILER})
    set(CPP_INC -Wp,-I,)
    # Avoid clang warning on perl ''
    set(CPP_IGNORE_EMPTY -Wno-invalid-pp-token)
  else (APPLE)
    # "gcc -E" on a non-.c-extension file gives message:
    #   "linker input file unused because linking not done"
    # and doesn't produce any output, so we must use cpp for our .asm files.
    # we assume it's in the same dir.
    get_filename_component(compiler_path ${CMAKE_C_COMPILER} PATH)
    find_program(CMAKE_CPP_FOR_OPS cpp HINTS "${compiler_path}"
      DOC "path to C preprocessor")
    if (cpp-NOTFOUND OR NOT EXISTS "${CMAKE_CPP_FOR_OPS}")
      message(FATAL_ERROR "cpp is required to build")
    endif (cpp-NOTFOUND OR NOT EXISTS "${CMAKE_CPP_FOR_OPS}")
    mark_as_advanced(CMAKE_CPP_FOR_OPS)
    set(CPP_INC -I)
  endif (APPLE)
  set(CPP_NO_LINENUM -P)
else (UNIX)
  set(CMAKE_CPP_FOR_OPS ${CMAKE_C_COMPILER})
  set(CPP_NO_LINENUM /EP)
  set(CPP_INC /I)
endif (UNIX)

# I would share this name w/ drmemory.pl but it's a pain to configure_file
# or generate the perl script as we also have to compile it for pp.
set(options_for_perl "${PROJECT_BINARY_DIR}/${BUILD_BIN}/options-perl.pl")
add_custom_target(options_perl ALL DEPENDS "${options_for_perl}")
add_custom_command(
  OUTPUT "${options_for_perl}"
  DEPENDS "${PROJECT_SOURCE_DIR}/common/options-perl.c"
  "${PROJECT_SOURCE_DIR}/${tooldir}/optionsx.h"
  COMMAND ${CMAKE_CPP_FOR_OPS}
  ARGS -E ${CPP_NO_LINENUM} "${PROJECT_SOURCE_DIR}/common/options-perl.c"
  ${CPP_INC}${PROJECT_SOURCE_DIR}/${tooldir} ${DEFINES}
  ${CPP_IGNORE_EMPTY} > "${options_for_perl}"
  VERBATIM)

# options_for_docs is built in docs/CMakeLists.txt since custom commands
# are directory-local

##################################################
# utility functions

function (append_property_string type target name value)
  # XXX: if we require cmake 2.8.6 we can simply use APPEND_STRING
  get_property(cur ${type} ${target} PROPERTY ${name})
  if (cur)
    set(value "${cur} ${value}")
  endif (cur)
  set_property(${type} ${target} PROPERTY ${name} "${value}")
endfunction (append_property_string)

function (set_output_dirs dir)
  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${dir}" PARENT_SCOPE)
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${dir}" PARENT_SCOPE)
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}" PARENT_SCOPE)
  if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
    # we don't support the Debug and Release subdirs
    foreach (config ${CMAKE_CONFIGURATION_TYPES})
      string(TOUPPER "${config}" config_upper)
      set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config_upper}
        "${dir}" PARENT_SCOPE)
      set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config_upper}
        "${dir}" PARENT_SCOPE)
      set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config_upper}
        "${dir}" PARENT_SCOPE)
    endforeach ()
  endif ()
endfunction (set_output_dirs)

##################################################
# find DynamoRIO so tests has its path, but don't include it or run
# its configure commands to avoid changing cflags:

# DR clobbers the global cflags, so we save and then restore them for
# our tests (and ourselves for non-pre-built DR).
# configure_DynamoRIO_client() also does so we do this even for pre-built DR
# for tests.
foreach (config "" ${CMAKE_BUILD_TYPE} ${CMAKE_CONFIGURATION_TYPES})
  if ("${config}" STREQUAL "")
    set(config_upper "")
  else ("${config}" STREQUAL "")
    string(TOUPPER "_${config}" config_upper)
  endif ("${config}" STREQUAL "")
  foreach (var CMAKE_C_FLAGS${config_upper};CMAKE_CXX_FLAGS${config_upper})
    set(SAVE_${var} "${${var}}")
  endforeach (var)
endforeach (config)

# we write DynamoRIO_DIR to the cache, so on re-interpreting the file later
# we can't tell whether the user set a value or not. we could
# use a differently-named var for the user but instead we check vs
# the local build path to avoid breaking existing scripts.
set(LOCAL_DynamoRIO_DIR "${PROJECT_BINARY_DIR}/dynamorio/cmake")
if (DEFINED DynamoRIO_DIR AND NOT "${DynamoRIO_DIR}" STREQUAL "${LOCAL_DynamoRIO_DIR}")
  set(USER_SPECIFIED_DynamoRIO_DIR ON)
else ()
  set(USER_SPECIFIED_DynamoRIO_DIR OFF)
endif ()

# when updating this, also update the git submodule
set(DynamoRIO_VERSION_REQUIRED "7.91.18077")

set(DR_install_dir "dynamorio")

set(DynamoRIO_PAGE_SIZE_COMPATIBILITY ON)

set(DynamoRIO_LOG_COMPATIBILITY ON)

if (USER_SPECIFIED_DynamoRIO_DIR)
  # i#67: relative dirs are not really supported in find_package: they're
  # relative to source dir not build dir, so we change that here.
  # we can't do get_filename_component(... ABSOLUTE) b/c it's relative to
  # source dir as well.
  if ("${DynamoRIO_DIR}" MATCHES "^\\.\\.")
    get_filename_component(DynamoRIO_DIR "${PROJECT_BINARY_DIR}/${DynamoRIO_DIR}" ABSOLUTE)
  endif ()
  # exit if it doesn't exist since very misleading if find_package() goes
  # and finds some other version from what was requested
  if (NOT EXISTS "${DynamoRIO_DIR}/DynamoRIOConfig.cmake")
    message(FATAL_ERROR "${DynamoRIO_DIR}/DynamoRIOConfig.cmake does not exist: invalid DynamoRIO_DIR")
  endif ()

  if (BUILDING_SUB_PACKAGE)
    message(FATAL_ERROR "Sub-package not supported with pre-built DR")
  endif ()

  message(STATUS "Attempting to use pre-built DynamoRIO: ${DynamoRIO_DIR}")
  find_package(DynamoRIO ${DynamoRIO_VERSION_REQUIRED})
  if (NOT DynamoRIO_FOUND)
    message(FATAL_ERROR "DynamoRIO package required to build")
  endif(NOT DynamoRIO_FOUND)
  # from here on use what was found, not what was passed in
  get_filename_component(DynamoRIO_DIR "${DynamoRIO_CONFIG}" PATH)
  # preserve real value in the cache so it's easy to tell
  set(DynamoRIO_DIR "${DynamoRIO_DIR}" CACHE PATH "Path to DynamoRIO.")
  message(STATUS "DynamoRIO that matches: ${DynamoRIO_VERSION} in ${DynamoRIO_DIR}")
else (USER_SPECIFIED_DynamoRIO_DIR)
  # Build from our local copy of the sources, coming from a git submodule: i#74.
  set(DynamoRIO_DIR "${LOCAL_DynamoRIO_DIR}" CACHE PATH "Path to DynamoRIO.")
  message(STATUS "Building DynamoRIO from local sources ${DynamoRIO_DIR}")

  # We include DynamoRIO as a subdir here to make it easy to use the
  # DynamoRIOConfig.cmake at configure time: however, that also means we have
  # potential conflicts in CMake's global option and target space.
  # Ideally, DynamoRIO would prefix all its options and targets with "DR_"
  # or something.  For now we live w/ the ugliness.
  # (An alternative would be to use the ExternalProject feature: but
  # then we don't have DynamoRIOConfig.cmake at config time and we'd
  # either need a parent build project or to hack our find_package().)

  # it seems that we must set these in the cache for the subproj to see them:
  set(BUILD_DOCS OFF CACHE BOOL "DynamoRIO option: build client samples")
  set(BUILD_SAMPLES OFF CACHE BOOL "DynamoRIO option: build documentation")
  # our local DR build matches our own build type
  if ("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
    set(DEBUG ON CACHE BOOL "DynamoRIO option: debug build")
    set(INTERNAL ON CACHE BOOL "DynamoRIO option: internal build")
  endif ("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
  if (DRM_COPY_TO_DEVICE)
    set(DR_COPY_TO_DEVICE ON CACHE BOOL "DynamoRIO option: copy to Android")
    get_filename_component(builddir ${PROJECT_BINARY_DIR} NAME)
    set(DR_DEVICE_BASEDIR "${DRM_DEVICE_BASEDIR}/${builddir}" CACHE STRING
      "DynamoRIO option: Android path")
  endif ()
  # We do not want DR install, except for the targets we need for our
  # multi-export-set install of the DRMF.  We need those targets
  # even for BUILDING_SUB_PACKAGE.
  set(DO_DR_INSTALL OFF)
  set(DO_DR_INSTALL_TARGETS ON)
  # Stick the binaries somewhere outside of the install dir.
  # However, NSIS won't allow an absolute path (i#1099).
  # So for an automated package.cmake build via cpack, we use .. which
  # is fine in the package dir structure.  We don't want .. for a
  # user-specified destination of course.  We simply don't
  # support creating a package manually outside of package.cmake.
  if (BUILDING_PACKAGE)
    set(DR_INSTALL_TARGETS_DEST ../ignored-installs)
  else ()
    set(DR_INSTALL_TARGETS_DEST ${PROJECT_BINARY_DIR}/dynamorio/installs)
  endif ()
  # i#1449: we need cmake to remove the absolute path from the LC_LOAD_DYLIB
  # entries, which only happens on install.  We can't easily do a two-step
  # install b/c subdirs go after the main dir -- so we have DR cooperation.
  set(DR_INSTALL_DEPLOY_BIN_DEST ${DR_install_dir}/${BIN_ARCH})

  add_subdirectory(dynamorio)

  # don't show DR options in drmem cmake list
  # to really hide we should mark as INTERNAL but not worth it since would
  # have to do for all of DR's many options.
  # see comment above about DR prefixing its options.
  mark_as_advanced(BUILD_CORE BUILD_DOCS BUILD_SAMPLES BUILD_EXT BUILD_TESTS
      BUILD_TOOLS DEBUG INTERNAL)

  # do not import dynamorio lib target: we'd end up w/ duplicate
  # dynamorio targets
  set(DynamoRIO_INTERNAL ON)
  # our included DynamoRIO project will set DynamoRIO_SOURCE_DIR in cache
  # for us so we'll get proper include dirs for extensions.

  find_package(DynamoRIO ${DynamoRIO_VERSION_REQUIRED})
  if (NOT DynamoRIO_FOUND OR
      # make sure it didn't go find some other pre-built version after
      # seeing that the local one is somehow not suitable
      NOT "${DynamoRIO_CONFIG}" STREQUAL "${DynamoRIO_DIR}/DynamoRIOConfig.cmake")
    message(FATAL_ERROR "Local DynamoRIO mis-configured")
  endif ()

  # Restore global flags
  foreach (config "" ${CMAKE_BUILD_TYPE} ${CMAKE_CONFIGURATION_TYPES})
    if ("${config}" STREQUAL "")
      set(config_upper "")
    else ("${config}" STREQUAL "")
      string(TOUPPER "_${config}" config_upper)
    endif ("${config}" STREQUAL "")
    foreach (var CMAKE_C_FLAGS${config_upper};CMAKE_CXX_FLAGS${config_upper})
      set(${var} "${SAVE_${var}}")
    endforeach (var)
  endforeach (config)
endif (USER_SPECIFIED_DynamoRIO_DIR)

if (USER_SPECIFIED_DynamoRIO_DIR)
  # if we're building from our own DR, DR adds this option for us
  option(GENERATE_PDBS "generate Windows debug information" ON)
  mark_as_advanced(GENERATE_PDBS)
endif (USER_SPECIFIED_DynamoRIO_DIR)

# This must be before any add_library() or add_executable()
# but that means for local DR sources we haven't yet included
# DR's option(), so we check whether defined.
if (DEFINED GENERATE_PDBS AND NOT GENERATE_PDBS)
  # Default from cmake in DEBUG and RELWITHDEBINFO has /debug
  foreach (var CMAKE_C_FLAGS;CMAKE_CXX_FLAGS;
      CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER};
      CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER};
      # tests are always built as Debug
      CMAKE_C_FLAGS_DEBUG;CMAKE_CXX_FLAGS_DEBUG)
    string(REGEX REPLACE "/Zi" "" ${var} "${${var}}")
  endforeach ()
  foreach (var CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER};
      CMAKE_MODULE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER};
      CMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER};
      # tests are always built as Debug
      CMAKE_EXE_LINKER_FLAGS_DEBUG;
      CMAKE_MODULE_LINKER_FLAGS_DEBUG;
      CMAKE_SHARED_LINKER_FLAGS_DEBUG)
    string(REGEX REPLACE "/debug" "" ${var} "${${var}}")
  endforeach ()
endif (DEFINED GENERATE_PDBS AND NOT GENERATE_PDBS)

# Shrink binaries and pdbs (/Gy should already be there)
if (WIN32)
  foreach (var CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER};
      CMAKE_MODULE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER};
      CMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER})
    set(${var} "${${var}} /opt:ref /opt:icf /pdbcompress")
  endforeach ()
endif ()

if (APPLE)
  # install_name_tool needs write access (i#1372)
  set(owner_access OWNER_READ OWNER_WRITE)
else (APPLE)
  set(owner_access OWNER_READ)
endif (APPLE)

##################################################
# now that we have ${DynamoRIO_DIR} we can configure docs

find_package(Doxygen)
if (NOT DOXYGEN_FOUND)
  # We'd like to require doxygen for Windows pre-commit suite but it's not
  # installed on our bots so we live with just Linux catching docs errors.
  # Ditto for Mac.
  if (TEST_SUITE AND UNIX AND NOT APPLE)
    message(FATAL_ERROR "doxygen is required to build the documentation")
  else ()
    # Non-fatal for a single, un-official build, or on Windows
    message(WARNING "doxygen not found: documentation will NOT be built")
  endif ()
else ()
  add_subdirectory(docs)
endif ()

##################################################
# assembly support

# set up assembly support and CMAKE_CPP
set(old_debug ${DEBUG})
set(DEBUG ON) # ensure we get debug info
include(${DynamoRIO_DIR}/cpp2asm_support.cmake)
set(DEBUG ${old_debug})

if (UNIX)
  if (NOT CMAKE_ASM_SUPPORTS_INTEL_SYNTAX)
    message(FATAL_ERROR "${CMAKE_ASM_COMPILER} does not support required flags")
  endif (NOT CMAKE_ASM_SUPPORTS_INTEL_SYNTAX)
endif (UNIX)
# for cpp2asm_defines.h
include_directories(${DynamoRIO_DIR})

# XXX: even if we went to a configure.h, we wouldn't have the DR platform
# defines there unless we duplicated them.
if (UNIX)
  if (APPLE)
    set(DEFINES ${DEFINES} -DASSEMBLE_WITH_NASM)
  else (APPLE)
    set(DEFINES ${DEFINES} -DASSEMBLE_WITH_GAS)
  endif (APPLE)
else (UNIX)
  set(DEFINES ${DEFINES} -DASSEMBLE_WITH_MASM)
endif (UNIX)
get_DynamoRIO_defines(DR_DEFINES OFF)
# We need defines to be a list to pass as separate args to custom command.
# We assume none have spaces inside them which seems reasonable.
string(REPLACE " " ";" DR_DEFINES "${DR_DEFINES}")
set(asm_defs ${DR_DEFINES} ${DEFINES} -I "${DynamoRIO_DIR}")

set(asm_deps "${DynamoRIO_DIR}/cpp2asm_defines.h")

if (ARM)
  set(asm_file "asm_utils_arm.asm")
else ()
  set(asm_file "asm_utils_x86.asm")
endif ()
add_asm_target(common/${asm_file} asm_utils_src asm_utils_tgt ""
  "${asm_defs}" "${asm_deps}")

##################################################

set(DrMemory_INTERNAL ON) # Do not import exported targets.

if (ANDROID)
  # We cache this for us in sub-projects like framework/samples/
  set(TOP_BINARY_DIR ${PROJECT_BINARY_DIR})
  get_filename_component(builddir ${TOP_BINARY_DIR} NAME)
  set(DRM_DEVICE_BINARY_DIR ${DRM_DEVICE_BASEDIR}/${builddir})
endif ()

function (copy_target_to_device target)
  if (DRM_COPY_TO_DEVICE)
    DynamoRIO_copy_target_to_device(${target} ${DRM_DEVICE_BASEDIR} "${_DR_location_suffix}")
  endif ()
endfunction (copy_target_to_device)

function (copy_file_to_device local_path)
  if (DRM_COPY_TO_DEVICE)
    file(RELATIVE_PATH relpath "${TOP_BINARY_DIR}" "${local_path}")
    execute_process(
      COMMAND ${ADB} push ${local_path} ${DRM_DEVICE_BINARY_DIR}/${relpath}
      RESULT_VARIABLE adb_result ERROR_VARIABLE adb_err OUTPUT_QUIET)
    if (adb_result)
      message(FATAL_ERROR "*** Failed to adb push ${local_path}: ${adb_err} ***\n")
    endif ()
  endif ()
endfunction (copy_file_to_device)

function (get_target_path_for_execution out target)
  if (ANDROID)
    DynamoRIO_get_target_path_for_execution(local ${target} ${DRM_DEVICE_BASEDIR} "${_DR_location_suffix}")
  else ()
    DynamoRIO_get_target_path_for_execution(local ${target} "" "${_DR_location_suffix}")
  endif ()
  set(${out} ${local} PARENT_SCOPE)
endfunction (get_target_path_for_execution)

function (convert_local_path_to_device_path out local_path)
  if (ANDROID)
    file(RELATIVE_PATH relpath "${TOP_BINARY_DIR}" "${local_path}")
    set(local ${DRM_DEVICE_BINARY_DIR}/${relpath})
  else ()
    set(local ${local_path})
  endif ()
  set(${out} ${local} PARENT_SCOPE)
endfunction (convert_local_path_to_device_path)

function (prefix_cmd_if_necessary cmd_out use_ats cmd_in)
  DynamoRIO_prefix_cmd_if_necessary(local ${use_ats} ${cmd_in} ${ARGN})
  set(${cmd_out} ${local} PARENT_SCOPE)
endfunction (prefix_cmd_if_necessary)

function (copy_and_adjust_drpaths basedir target)
  if (ANDROID AND DRM_COPY_TO_DEVICE)
    file(GLOB drpaths ${target}/*${target}*.drpath)
    foreach(drpath ${drpaths}) # we only expect one though
      file(READ ${drpath} contents)
      string(REPLACE "${PROJECT_BINARY_DIR}" "${DRM_DEVICE_BINARY_DIR}"
        contents ${contents})
      file(WRITE ${drpath} ${contents})
      copy_file_to_device(${drpath})
    endforeach ()
  endif ()
endfunction ()

if (WIN32)
  set(FLAG_DISABLE_FPO "/Oy-")
else (WIN32)
  set(FLAG_DISABLE_FPO "-fno-omit-frame-pointer")
endif (WIN32)

function(append_src_compile_flags srcfile new_flags)
  get_source_file_property(cur_flags ${srcfile} COMPILE_FLAGS)
  # XXX: if we require cmake 2.8.6 we can simply use APPEND_STRING
  if (NOT cur_flags)
    set(cur_flags "")
  endif (NOT cur_flags)
  set_source_files_properties(${srcfile} PROPERTIES
    COMPILE_FLAGS "${cur_flags} ${new_flags}")
endfunction(append_src_compile_flags)

# new set_property() doesn't want -D but our cpp invocation above does
string(REGEX REPLACE "-D" "" DEFINES_NO_D "${DEFINES}")
string(REGEX REPLACE "-D" "" DR_DEFINES_NO_D "${DR_DEFINES}")

option(BUILD_TOOL_TESTS "build Dr. Memory/Dr. Heapstat tests" ON)

if (TOOL_DR_HEAPSTAT)
  # Dr. Heapstat
  set(srcs
    drheapstat/drheapstat.c
    drheapstat/staleness.c
    common/alloc.c
    common/alloc_unopt.c
    common/alloc_replace.c
    common/heap.c
    common/callstack.c
    common/utils.c
    common/utils_shared.c
    ${asm_utils_src}
    common/redblack.c
    common/crypto.c
    # For leak checking we need stack.c but it pulls in the inter-dependent
    # slowpath, fastpath, and shadow: we'll want those for staleness anyway.
    # Looking more and more like Dr. Memory!
    drmemory/annotations.c
    drmemory/leak.c
    drmemory/options.c
    drmemory/stack.c
    drmemory/instru.c
    drmemory/spill.c
    drmemory/slowpath.c
    drmemory/fastpath.c
    drmemory/shadow.c
    drmemory/perturb.c)
else (TOOL_DR_HEAPSTAT)
  # Dr. Memory
  set(srcs
    drmemory/annotations.c
    drmemory/drmemory.c
    drmemory/instru.c
    drmemory/spill.c
    drmemory/slowpath.c
    drmemory/fastpath.c
    drmemory/stack.c
    drmemory/shadow.c
    drmemory/options.c
    drmemory/pattern.c
    common/alloc.c
    common/alloc_unopt.c
    common/alloc_replace.c
    common/heap.c
    common/callstack.c
    drmemory/alloc_drmem.c
    drmemory/syscall.c
    drmemory/report.c
    drmemory/replace.c
    drmemory/leak.c
    drmemory/memlayout.c
    drmemory/perturb.c
    common/utils.c
    common/utils_shared.c
    ${asm_utils_src}
    common/redblack.c
    common/crypto.c
    drmemory/fuzzer.c)
  if (UNIX)
    if (APPLE)
      set(srcs ${srcs} drmemory/syscall_macos.c)
    else (APPLE)
      set(srcs ${srcs} drmemory/syscall_linux.c)
    endif (APPLE)
  else (UNIX)
    set(srcs ${srcs} drmemory/syscall_windows.c)
    set(srcs ${srcs} drmemory/syscall_wingdi.c)
    set(srcs ${srcs} drmemory/gdicheck.c)
    set(srcs ${srcs} drmemory/handlecheck.c)
  endif (UNIX)
  set(scripts ${toolname}.pl)
endif (TOOL_DR_HEAPSTAT)

if (X86)
  set(srcs ${srcs} drmemory/slowpath_x86.c)
  set(srcs ${srcs} drmemory/stack_x86.c)
  set(srcs ${srcs} drmemory/fastpath_x86.c)
else ()
  set(srcs ${srcs} drmemory/slowpath_arm.c)
  set(srcs ${srcs} drmemory/stack_arm.c)
  set(srcs ${srcs} drmemory/fastpath_arm.c)
endif ()

if (WIN32)
  set(srcs ${srcs} make/resources.rc)
endif ()

if (NOT USE_DRSYMS)
  set(scripts ${scripts} postprocess.pl)
endif (NOT USE_DRSYMS)

if (USE_DRSYMS)
  # front-end needs to be named ${toolname}.exe and thus has ${toolsname}.pdb, so
  # we rename client lib (plus cmake best w/o same-name targets)
  set(client_target "${toolname}lib")
  if (TOOL_DR_MEMORY)
    set(frontend_srcs drmemory/frontend.c drmemory/options.c)
  else (TOOL_DR_MEMORY)
    set(frontend_srcs drheapstat/drheapstat_frontend.c)
  endif (TOOL_DR_MEMORY)
  if (WIN32)
    set(frontend_srcs ${frontend_srcs} make/resources.rc)
  endif ()
  add_executable(${toolname} ${frontend_srcs})
  if (ANDROID AND TOOL_DR_MEMORY)
    # There's no DT_RPATH support so we have a script set the load path.
    # To avoid confusion we rename the exe.
    set(frontend_name "launcher")
    set_target_properties(${toolname} PROPERTIES OUTPUT_NAME "${frontend_name}")
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${tooldir}/launcher_android.sh"
      "${PROJECT_BINARY_DIR}/${BUILD_BIN}/${toolname}" @ONLY)
    copy_file_to_device("${PROJECT_BINARY_DIR}/${BUILD_BIN}/${toolname}")
  endif ()
  if (WIN32)
    target_link_libraries(${toolname} dbghelp)
  endif (WIN32)
  copy_target_to_device(${toolname})
else (USE_DRSYMS)
  set(client_target ${toolname})
endif (USE_DRSYMS)

macro(set_library_version target number)
  # We only set the version/soversion on Windows to avoid many
  # negatives (DRi#1374, DRi#2127, Android "adb push" not supporting
  # symlinks, etc.) and very few positives on UNIX platforms.  DR's
  # loader and the DRMF init code perform their own version checks for
  # client compatibility.
  if (WINDOWS)
    set_target_properties(${target} PROPERTIES VERSION ${number})
  endif ()
endmacro()

add_library(${client_target} SHARED ${srcs})
set_library_version(${client_target} ${TOOL_VERSION_NUMBER})
_DR_append_property_list(TARGET ${client_target} COMPILE_DEFINITIONS
  # If we end up wanting this for other DEFINES uses above we'll have to set
  # client_target earlier.  For now we only need for the C code.
  "${DEFINES_NO_D};CLIENT_LIBNAME=${client_target};RC_IS_TOOLLIB")
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
  # ensure race-free parallel builds
  add_dependencies(${client_target} ${asm_utils_tgt})
endif ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
copy_target_to_device(${client_target})

# We require frames in our replacement routines in order to reliably include
# the allocator routines in the callstack for i#639.  Xref i#958.
append_src_compile_flags(common/alloc_replace.c ${FLAG_DISABLE_FPO})

if (WIN32)
  # our addr2line for Windows
  add_executable(winsyms tools/winsyms.c make/resources.rc)
  target_link_libraries(winsyms dbghelp)
  _DR_append_property_list(TARGET winsyms COMPILE_DEFINITIONS
    # We need full defines to get version values for resources
    "${DEFINES_NO_D};RC_IS_WINSYMS")
  # configure_DynamoRIO_client clears global flags so add as additional
  # request static libc to avoid manifest files and libc portability issues
  string(REGEX REPLACE "/MD" "/MT" CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}
    "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}")
  set_source_files_properties(tools/winsyms.c PROPERTIES
    COMPILE_FLAGS "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}")

  # Helper tool to ensure we have version info in our binaries:
  add_executable(verinfo tools/verinfo.c make/resources.rc)
  target_link_libraries(verinfo version)
  _DR_append_property_list(TARGET verinfo COMPILE_DEFINITIONS
    "${DEFINES_NO_D};RC_IS_VERINFO")

  # i#1009c#4: auto-register Dr. Memory as a Visual Studio External Tool
  add_executable(vs_external_tool tools/vs_external_tool.c make/resources.rc)
  _DR_append_property_list(TARGET vs_external_tool COMPILE_DEFINITIONS
    "${DEFINES_NO_D};RC_IS_VS_EXTERNAL_TOOL")
endif (WIN32)

# we want a preferred base to avoid patching pcaches
set(DynamoRIO_SET_PREFERRED_BASE ON)
if (ANDROID)
  # i#1881: 0x73800000 seems to conflict w/ hardcoded Android mmaps.
  set(PREFERRED_BASE 0x17000000)
else ()
  set(PREFERRED_BASE 0x73800000)
endif ()

# we can handle being tied to a particular DR version (we already are)
set(DynamoRIO_FAST_IR ON)

set(DynamoRIO_REG_COMPATIBILITY ON)
if (NOT STATIC_DRSYMS)
  set(DynamoRIO_USE_LIBC OFF)
endif (NOT STATIC_DRSYMS)
configure_DynamoRIO_client(${client_target})
# i#277/PR 540817: features split into DynamoRIO Extensions
use_DynamoRIO_extension(${client_target} drcontainers)
# For efficiency, we use the static versions of DR's extensions.  We have the same
# LGPL license as drutil and drwrap so it works out.
use_DynamoRIO_extension(${client_target} drmgr_static)
use_DynamoRIO_extension(${client_target} drx_static)
use_DynamoRIO_extension(${client_target} drutil_static)
use_DynamoRIO_extension(${client_target} drwrap_static)
use_DynamoRIO_extension(${client_target} drreg_static)
# DRi#1829: we discussed invoking drcov as a separate client, which would require using
# all shared ext libs.  We ended up making drcovlib.
use_DynamoRIO_extension(${client_target} drcovlib_static)
set_target_properties(${client_target} PROPERTIES
  # dlls are put in runtime dir but we want lib dir
  RUNTIME_OUTPUT_DIRECTORY${location_suffix} "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
if (WIN32)
  # annoying to have ASLR and not on module list
  # plus, now we require the same base for pcache
  set(new_flags "/dynamicbase:no")

  # This flag is added by VS generators so we may as well add for Ninja
  set(new_flags "/nxcompat")

  # Reduce size by stripping out unused code (we don't seem to need /Gy for this,
  # and VS generators seem to set it already, so we add here for Ninja).
  # XXX: DRi#2167 is adding this for all clients so we can remove this once
  # we update to a recent DR.
  set(new_flags "${new_flags} /opt:ref")
  if (NOT DEBUG_BUILD)
    # Match DR's settings, though again it's not clear the benefit w/o /Gy.
    set(new_flags "${new_flags} /opt:icf")
  endif()

  if (NOT DEFINED GENERATE_PDBS OR GENERATE_PDBS)
    set(new_flags "${new_flags} /debug")
  endif ()
  get_target_property(cur_flags ${client_target} LINK_FLAGS)
  if (NOT cur_flags)
    set(cur_flags "")
  endif (NOT cur_flags)
  set_target_properties(${client_target} PROPERTIES
    LINK_FLAGS "${cur_flags} ${new_flags}")
endif (WIN32)
if (USE_DRSYMS)
  if (DEFINED DynamoRIO_RPATH)
    set(old_rpath ${DynamoRIO_RPATH})
  else ()
    set(old_rpath OFF)
  endif ()
  set(DynamoRIO_RPATH ON)
  configure_DynamoRIO_standalone(${toolname})
  set(DynamoRIO_RPATH ${old_rpath})
  target_link_libraries(${toolname} drinjectlib drconfiglib drfrontendlib
    drsyscall_static)
  if (WIN32)
    set_target_properties(${toolname} PROPERTIES
      VERSION ${TOOL_VERSION_NUMBER})
    _DR_append_property_list(TARGET ${toolname}
      COMPILE_DEFINITIONS "${DEFINES_NO_D};RC_IS_FRONTEND")
  else (WIN32)
    DynamoRIO_add_rel_rpaths(${toolname} drinjectlib)
    DynamoRIO_add_rel_rpaths(${toolname} drconfiglib)
    _DR_append_property_list(TARGET ${toolname} COMPILE_DEFINITIONS "${DEFINES_NO_D}")
  endif (WIN32)
  if (STATIC_DRSYMS)
    use_DynamoRIO_extension(${client_target} drsyms_static)
  else ()
    # N.B.: static drsyms gives us kernel32 imports from elftoolchain
    # libc use.  If we end up needing late kernel32 use for early
    # injection we may want to go back to dynamic drsyms (and delayed
    # dr_enable_console_printing()).
    use_DynamoRIO_extension(${client_target} drsyms)
  endif ()
endif (USE_DRSYMS)
if (WIN32)
  # We need to link with ntdll.lib and dbghelp.lib which we get from DR
  if (USER_SPECIFIED_DynamoRIO_DIR)
    # XXX i#1651: Replace this LOCATION with a generator expression.
    # This is the only one left.
    # We may want to drop support for USER_SPECIFIED_DynamoRIO_DIR in any case
    # which is why no effort was put in to clean this up earlier.
    cmake_policy(SET CMP0026 OLD)
    get_target_property(libbase drinjectlib LOCATION)
    get_filename_component(libpath ${libbase} PATH)
    set(ntimp_lib "${libpath}/ntdll_imports.lib")
    set(dbghelp_lib "${libpath}/dbghelp_imports.lib")
  else ()
    # DR now has ntdll_imports as a normal lib target
    set(ntimp_lib ntdll_imports)
    # XXX: This relies on knowing where DR puts it.
    # One option is ExternalProject to install the local DR first: xref i#1061.
    set(dbghelp_lib "${PROJECT_BINARY_DIR}/dynamorio/ext/drsyms/dbghelp_imports.lib")
    if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
      # for parallel build correctness we need a target dependence
      add_dependencies(${client_target} ntdll_imports)
      if (TOOL_DR_MEMORY)
        add_dependencies(${toolname} dbghelp_tgt)
      endif ()
    endif ()
  endif ()

  # Front-end uses dbghelp routines not in VS2005 so we link w/ DR's import lib.
  target_link_libraries(${toolname} ${dbghelp_lib})

  # we have to link with ntdll AFTER any libcmt.lib from configure_DynamoRIO_client
  # or use_DynamoRIO_extension (w/ static libs)
  target_link_libraries(${client_target} ${ntimp_lib})
  # we used to statically link with msvcrt.lib for vc /O2's use of __aulldvrm,
  # and it didn't add any dynamic dependence on libc, except now that we
  # use static extension libraries it does add msvcr*.dll imports!
  # so I removed "msvcrt" from target_link_libraries() and so far haven't
  # hit the __aulldvrm issue.
endif (WIN32)

# Build Qt Visualizer
if (TOOL_DR_HEAPSTAT)
  find_package(Qt5Widgets QUIET)
  if (NOT Qt5Widgets_FOUND)
    message(STATUS "Could NOT find Qt 5: Dr. Heapstat visualizer will not be built")
    message(STATUS
      "Point CMake variable Qt5Widgets_DIR at the Qt5WidgetsConfig.cmake directory")
  else (NOT Qt5Widgets_FOUND)
    message(STATUS "Found Qt 5: Dr. Heapstat visualizer will be built")
    add_subdirectory(drheapstat/visualizer)
  endif (NOT Qt5Widgets_FOUND)
endif (TOOL_DR_HEAPSTAT)

#
##################################################

# must be AFTER DR local build since affects subdirs
include_directories(common ${tooldir} "third_party/valgrind")

if (UNIX)
  set(DISABLE_OPTS "-O0")
else (UNIX)
  set(DISABLE_OPTS "/Od")
endif (UNIX)
append_src_compile_flags(common/alloc_unopt.c "${DISABLE_OPTS}")
if (UNIX)
  # i#1776: avoid an infinite loop in replace_memset.
  # We have no per-function optimization so we apply to the whole file.
  append_src_compile_flags(drmemory/replace.c "${DISABLE_OPTS}")
endif ()

# symbol query tool
set(symquery_srcs tools/symquery.c)
if (WIN32)
  set(symquery_srcs ${symquery_srcs} make/resources.rc)
endif ()
add_executable(symquery ${symquery_srcs})
if (DEFINED DynamoRIO_RPATH)
  set(old_rpath ${DynamoRIO_RPATH})
else ()
  set(old_rpath OFF)
endif ()
set(DynamoRIO_RPATH ON)
configure_DynamoRIO_standalone(symquery)
use_DynamoRIO_extension(symquery drsyms_static)
set(DynamoRIO_RPATH ${old_rpath})
# drfrontendlib depends on drinjectlib, DR-i#1409 should be the solution
target_link_libraries(symquery drinjectlib drfrontendlib)
if (WIN32)
  set_target_properties(symquery PROPERTIES VERSION ${TOOL_VERSION_NUMBER})
  _DR_append_property_list(TARGET symquery COMPILE_DEFINITIONS
    # We need full defines to get version values for resources
    "${DEFINES_NO_D};RC_IS_SYMQUERY")
else (WIN32)
  DynamoRIO_add_rel_rpaths(symquery drinjectlib)
endif (WIN32)

# should go into a configure.h if we get enough of these
set(script_aux "")
if (PERL_TO_EXE)
  foreach (pl ${scripts})
    get_filename_component(base ${pl} NAME_WE)
    if ("${pl}" MATCHES "${toolname}.pl")
      set(EXE_DEST "${PROJECT_BINARY_DIR}/${BUILD_BIN_PREFIX}")
      set(script_main ${EXE_DEST}/${base}.exe)
    else ()
      set(EXE_DEST "${PROJECT_BINARY_DIR}/${BUILD_BIN}")
      set(script_aux ${script_aux} ${EXE_DEST}/${base}.exe)
    endif ()
    add_custom_target(${base}_exe ALL DEPENDS ${EXE_DEST}/${base}.exe)
    add_custom_command(
      OUTPUT ${EXE_DEST}/${base}.exe
      DEPENDS ${PROJECT_SOURCE_DIR}/${tooldir}/${pl}
      COMMAND ${PERL_EXECUTABLE}
      # If do glob() w/ spaces in path, need Text::ParseWords, but pp
      # doesn't find it as a dependence automatically.
      # FIXME: use --gui?
      ARGS ${PERL_PP} -M Text::ParseWords -o ${EXE_DEST}/${base}.exe
        ${PROJECT_SOURCE_DIR}/${tooldir}/${pl} ${options_for_perl}
      VERBATIM)
  endforeach (pl)
endif (PERL_TO_EXE)

# support running out of build dir
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/logs")
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/logs/codecache")
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/logs/dynamorio")
file(WRITE "${PROJECT_BINARY_DIR}/logs/README" "Directory for logs") # for adb push
copy_file_to_device("${PROJECT_BINARY_DIR}/logs")
if (NOT PERL_TO_EXE AND NOT USE_DRSYMS)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${tooldir}/${toolname}.pl"
    "${PROJECT_BINARY_DIR}/${BUILD_BIN}/${toolname}.pl" @ONLY)
  if (NOT USE_DRSYMS)
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${tooldir}/postprocess.pl"
      "${PROJECT_BINARY_DIR}/${BUILD_BIN}/postprocess.pl" COPYONLY)
    set(script_aux "${CMAKE_CURRENT_SOURCE_DIR}/${tooldir}/postprocess.pl")
  endif ()
  set(script_main "${PROJECT_BINARY_DIR}/${BUILD_BIN}/${toolname}.pl"
    "${options_for_perl}")
endif ()
if (WIN32)
  # copy for winsyms and symquery
  configure_file("${DBGHELP_DLL}"
    "${PROJECT_BINARY_DIR}/${BUILD_BIN}/dbghelp.dll" COPYONLY)
  # Copy symsrv.dll so we can fetch files from the frontend.
  configure_file("${SYMSRV_DLL}"
    "${PROJECT_BINARY_DIR}/${BUILD_BIN}/symsrv.dll" COPYONLY)
  # Create symsrv.yes to avoid the EULA dialog.
  file(WRITE "${PROJECT_BINARY_DIR}/${BUILD_BIN}/symsrv.yes" "")
  if (USER_SPECIFIED_DynamoRIO_DIR)
    # already built so we can copy at config time
    configure_file("${DynamoRIO_DIR}/../${LIB_ARCH}/drconfiglib.dll"
      "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drconfiglib.dll" COPYONLY)
    configure_file("${DynamoRIO_DIR}/../${LIB_ARCH}/drinjectlib.dll"
      "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drinjectlib.dll" COPYONLY)
    if (USE_DRSYMS)
      configure_file("${DynamoRIO_DIR}/../${BIN_ARCH}/drconfig.exe"
        "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drconfig.exe" COPYONLY)
      if (NOT STATIC_DRSYMS AND TOOL_DR_MEMORY)
        # symquery needs drsyms.dll in same dir
        configure_file("${DynamoRIO_DIR}/../ext/${LIB_ARCH}/${build_type}/drsyms.dll"
          "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drsyms.dll" COPYONLY)
      endif (NOT STATIC_DRSYMS AND TOOL_DR_MEMORY)
      # symquery needs dynamorio.dll in same dir
      # frontend now imports from DR (i#885)
      configure_file("${DynamoRIO_DIR}/../${LIB_ARCH}/${build_type}/dynamorio.dll"
        "${PROJECT_BINARY_DIR}/${BUILD_BIN}/dynamorio.dll" COPYONLY)
    endif (USE_DRSYMS)
  else (USER_SPECIFIED_DynamoRIO_DIR)
    # XXX: I can't get "TARGET drconfiglib POST_BUILD" to work, maybe b/c
    # the target is in a subdir?
    set(drconfiglib_copy "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drconfiglib.dll")
    add_custom_target(drconfiglib_copy_tgt ALL DEPENDS "${drconfiglib_copy}")
    add_custom_command(OUTPUT "${drconfiglib_copy}" DEPENDS drconfiglib
      COMMAND ${CMAKE_COMMAND}
      ARGS -E copy "${DynamoRIO_DIR}/../${LIB_ARCH}/drconfiglib.dll"
      "${drconfiglib_copy}" VERBATIM)
    set(drinjectlib_copy "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drinjectlib.dll")
    add_custom_target(drinjectlib_copy_tgt ALL DEPENDS "${drinjectlib_copy}")
    add_custom_command(OUTPUT "${drinjectlib_copy}" DEPENDS drinjectlib
      COMMAND ${CMAKE_COMMAND}
      ARGS -E copy "${DynamoRIO_DIR}/../${LIB_ARCH}/drinjectlib.dll"
      "${drinjectlib_copy}" VERBATIM)
    if (USE_DRSYMS)
      set(drconfigexe_copy "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drconfig.exe")
      add_custom_target(drconfigexe_copy_tgt ALL DEPENDS "${drconfigexe_copy}")
      add_custom_command(OUTPUT "${drconfigexe_copy}" DEPENDS drconfig
        COMMAND ${CMAKE_COMMAND}
        ARGS -E copy "${DynamoRIO_DIR}/../${BIN_ARCH}/drconfig.exe"
        "${drconfigexe_copy}" VERBATIM)
      if (NOT STATIC_DRSYMS AND TOOL_DR_MEMORY)
        # symquery needs drsyms.dll in same dir
        set(drsymsdll_copy "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drsyms.dll")
        add_custom_target(drsymsdll_copy_tgt ALL DEPENDS "${drsymsdll_copy}")
        add_custom_command(OUTPUT "${drsymsdll_copy}" DEPENDS drsyms
          COMMAND ${CMAKE_COMMAND}
          ARGS -E copy "${DynamoRIO_DIR}/../ext/${LIB_ARCH}/${build_type}/drsyms.dll"
        "${drsymsdll_copy}" VERBATIM)
      endif (NOT STATIC_DRSYMS AND TOOL_DR_MEMORY)
      # symquery needs dynamorio.dll in same dir
      # frontend now imports from DR (i#885)
      set(drdll_copy "${PROJECT_BINARY_DIR}/${BUILD_BIN}/dynamorio.dll")
      add_custom_target(drdll_copy_tgt ALL DEPENDS "${drdll_copy}")
      add_custom_command(OUTPUT "${drdll_copy}" DEPENDS dynamorio
        COMMAND ${CMAKE_COMMAND}
        ARGS -E copy "${DynamoRIO_DIR}/../${LIB_ARCH}/${build_type}/dynamorio.dll"
        "${drdll_copy}" VERBATIM)
    endif (USE_DRSYMS)
  endif (USER_SPECIFIED_DynamoRIO_DIR)
  if (USE_DRSYMS)
    # above copy is for winsyms and symquery, this is for drmemorylib
    configure_file("${DBGHELP_DLL}"
      "${PROJECT_BINARY_DIR}/${BUILD_LIB}/dbghelp.dll" COPYONLY)
    # We don't need to copy symsrv.dll because only the frontend uses it.
  endif (USE_DRSYMS)
else (WIN32)
  if (USER_SPECIFIED_DynamoRIO_DIR)
    # already built so we can copy at config time
    configure_file("${DynamoRIO_DIR}/../${BIN_ARCH}/drconfig"
      "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drconfig" COPYONLY)
  else (USER_SPECIFIED_DynamoRIO_DIR)
    set(drconfig_copy "${PROJECT_BINARY_DIR}/${BUILD_BIN}/drconfig")
    add_custom_target(drconfig_copy_tgt ALL DEPENDS "${drconfig_copy}")
    add_custom_command(OUTPUT "${drconfig_copy}"
      DEPENDS drconfig
      COMMAND ${CMAKE_COMMAND}
      ARGS -E copy "${DynamoRIO_DIR}/../${BIN_ARCH}/drconfig"
      "${drconfig_copy}" VERBATIM)
  endif (USER_SPECIFIED_DynamoRIO_DIR)
  if (TOOL_DR_MEMORY)
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/valgrind2drmemory.pl"
      "${PROJECT_BINARY_DIR}/${BUILD_BIN}/valgrind2drmemory.pl" COPYONLY)
  endif (TOOL_DR_MEMORY)
endif (WIN32)
if (TOOL_DR_HEAPSTAT)
  # we need a copy of Dr. Memory's postprocess for leaks (PR 536878)
  if (PERL_TO_EXE)
    message(FATAL_ERROR "perl2exe for Dr. Heapstat not supported")
  endif (PERL_TO_EXE)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/drmemory/postprocess.pl"
    "${PROJECT_BINARY_DIR}/${BUILD_BIN}/postleaks.pl" COPYONLY)
  set(script_aux ${script_aux} "${PROJECT_BINARY_DIR}/${BUILD_BIN}/postleaks.pl")
endif (TOOL_DR_HEAPSTAT)

# Dr. Memory and Dr. Heapstat use Dr. Memory's default suppressions
set(defsupp_out "${PROJECT_BINARY_DIR}/${BUILD_BIN}/suppress-default.txt")
if (WIN32)
  set(defsupp_in "${CMAKE_CURRENT_SOURCE_DIR}/drmemory/suppress-default.win.txt")
  configure_file("${defsupp_in}" "${defsupp_out}" COPYONLY)
else (WIN32)
  if (APPLE)
    set(defsupp_in "${CMAKE_CURRENT_SOURCE_DIR}/drmemory/suppress-default.mac.txt")
  else (APPLE)
    set(defsupp_in "${CMAKE_CURRENT_SOURCE_DIR}/drmemory/suppress-default.lin.txt")
  endif (APPLE)
  file(READ "${defsupp_in}" txt)
  file(WRITE "${defsupp_out}" "${txt}")
endif (WIN32)
copy_file_to_device("${defsupp_out}")

###########################################################################
# Dr. Memory Framework (DRMF) setup needed for tests

set(DRMF_BASEDIR "drmf")
set(DRMF_BINBASE "${LIB_ARCH}/${build_type}")
set(DRMF_INSTALL "${INSTALL_PREFIX}${DRMF_BASEDIR}")
set(DRMF_INSTALL_BIN "${DRMF_INSTALL}/${DRMF_BINBASE}")
set(DRMF_INSTALL_INC "${DRMF_INSTALL}/include")
set(framework_dir "${PROJECT_BINARY_DIR}/${DRMF_BASEDIR}")
set(framework_incdir "${framework_dir}/include")
set(framework_bindir "${framework_dir}/${DRMF_BINBASE}")

# Versioning: we use separate versioning for DRMF from the tool versioning.
# This is major*100 + minor.
set(DRMF_VERSION_DEFAULT "1.0.${VERSION_NUMBER_PATCHLEVEL}")
set(DRMF_VERSION "" CACHE STRING "DRMF version number: leave empty for default")
if ("${DRMF_VERSION}" STREQUAL "")
  set(DRMF_VERSION ${DRMF_VERSION_DEFAULT})
endif()
message(STATUS "DRMF version number: ${DRMF_VERSION}")
string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" DRMF_VERSION_MAJOR "${DRMF_VERSION}")
string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\..*" "\\1" DRMF_VERSION_MINOR "${DRMF_VERSION}")
# We use just major.minor for .so versioning (and no, SOVERSION is not sufficient
# here: we have to set VERSION to control the file name and import dependence).
string(REGEX REPLACE "^([0-9]+\\.[0-9]+)\\..*" "\\1"
  DRMF_VERSION_MAJOR_MINOR "${DRMF_VERSION}")
math(EXPR DRMF_VERSION_INTEGER "${DRMF_VERSION_MAJOR}*100 + ${DRMF_VERSION_MINOR}")
set(DRMF_VERSION_COMPAT 9)                # Oldest compatible version
set(DRMF_VERSION_CUR ${DRMF_VERSION_INTEGER}) # Current version

configure_file(framework/public.h
  ${framework_incdir}/drmemory_framework.h)
# TODO DRi#1672: Port DR's annotation infrastructure to AArchXX.
if (X86)
  configure_file(drmemory/annotations_public.h
    ${framework_incdir}/drmemory_annotations.h)
  configure_file(${DynamoRIO_DIR}/../include/annotations/dr_annotations_asm.h
    ${framework_incdir}/dr_annotations_asm.h)
  configure_file(${DynamoRIO_DIR}/../include/annotations/dr_annotations.h
    ${framework_incdir}/dr_annotations.h)
endif ()
include_directories(${framework_incdir})

if (NOT X86)
  # TODO DRi#1672: Port DR's annotation infrastructure to AArchXX.
  set(DR_ANNOTATIONS_SUPPORTED OFF)
else ()
  # Annotation support using DR's infrastructure (non-Valgrind-style).
  if (UNIX)
    set(annot_cflags "-O0 -Wno-unused-variable -Wno-return-type")
  else ()
    set(annot_cflags "/Od /Ob0 /GL- /wd4715")
  endif ()
  if (UNIX)
    # DRi#1799: clang prior to 9.0 does not support "asm goto" which is used by
    # the DR annotation infrastructure.  But, we can't just do a version check
    # because Apple's clang has a completely separate version number.
    try_compile(DR_ANNOTATIONS_SUPPORTED ${PROJECT_BINARY_DIR}/try_annot
      SOURCES ${PROJECT_SOURCE_DIR}/tests/memlayout.cpp
      ${PROJECT_SOURCE_DIR}/drmemory/annotations_public.c CMAKE_FLAGS
      -DINCLUDE_DIRECTORIES=${framework_incdir}
      -DCMAKE_C_FLAGS:STRING="${annot_cflags}"
      OUTPUT_VARIABLE try_output)
    if (DR_ANNOTATIONS_SUPPORTED)
      message(STATUS "DR annotations are supported")
    else ()
      message(STATUS "DR annotations are NOT supported: probably this is clang<9.0: |${try_output}|")
    endif ()
  else ()
    set(DR_ANNOTATIONS_SUPPORTED ON)
  endif ()
endif ()

###########################################################################
# Tests

# We need to add_subdirectory after we know where ntdll_imports.lib is.
# That means we've messed with global things like include dirs already,
# for configure_DynamoRIO_client(), but having the extension include
# dirs in there should be harmless to the tests.
if (BUILD_TOOL_TESTS)
  enable_testing()
  add_subdirectory(tests)
endif (BUILD_TOOL_TESTS)

###########################################################################
# Create tool config files for running with "drrun -t"

set(DRRUN_DIR "${PROJECT_BINARY_DIR}/drrun")
file(MAKE_DIRECTORY "${DRRUN_DIR}")

if (X64)
  set(BIT_SFX "64")
else ()
  set(BIT_SFX "32")
endif ()
set(DRRUN_SFX ".drrun${BIT_SFX}")
if (BUILDING_SUB_PACKAGE)
  set(DRRUN_INSTALL "tools/")
else ()
  set(DRRUN_INSTALL "${DR_install_dir}/tools/")
endif ()

function (create_drrun_file frontend file_out name) # option list follows name
  if (ANDROID AND TOOL_DR_MEMORY)
    set(frontend_name ${toolname}) # point at script, not launcher
  endif ()
  if (BUILDING_SUB_PACKAGE)
    set(DRRUN_TOP "FRONTEND_REL=${INSTALL_PREFIX}/${BUILD_BIN}")
  else ()
    set(DRRUN_TOP "FRONTEND_REL=../${BUILD_BIN}")
  endif ()
  set(DRRUN_FILE "${DRRUN_DIR}/${name}${DRRUN_SFX}")
  set(DRRUN_CONTENTS "# Dr. Memory tool config file\n")
  if (ANDROID AND TOOL_DR_MEMORY)
    # Point at script, not launcher.
    set(DRRUN_CONTENTS "${DRRUN_CONTENTS}${DRRUN_TOP}/${frontend}>\n")
  else ()
    set(DRRUN_CONTENTS "${DRRUN_CONTENTS}${DRRUN_TOP}/$<TARGET_FILE_NAME:${frontend}>\n")
  endif ()
  set(DRRUN_CONTENTS "${DRRUN_CONTENTS}TOOL_OP=-dr\n")
  set(DRRUN_CONTENTS "${DRRUN_CONTENTS}TOOL_OP_DR_PATH\n")
  set(DRRUN_CONTENTS "${DRRUN_CONTENTS}TOOL_OP_DR_BUNDLE=-dr_ops\n")
  foreach (op ${ARGN})
    set(DRRUN_CONTENTS "${DRRUN_CONTENTS}TOOL_OP=${op}\n")
  endforeach ()
  file(GENERATE OUTPUT ${DRRUN_FILE} CONTENT "${DRRUN_CONTENTS}")
  install(FILES "${DRRUN_FILE}" DESTINATION "${DRRUN_INSTALL}/")
  set(${file_out} ${DRRUN_FILE} PARENT_SCOPE)

  # DRi#1509: generate a list of tools for drrun usage messages
  if (NOT DEBUG_BUILD) # avoid dups
    install(CODE "file(APPEND \"\${CMAKE_INSTALL_PREFIX}/tools/list${BIT_SFX}\" \"${name}\\n\")")
  endif ()
endfunction (create_drrun_file)

create_drrun_file(${toolname} unused "drmemory")
if (NOT X64)
  create_drrun_file(${toolname} unused "drmemory_light" "-light")
endif ()
if (WIN32)
  create_drrun_file(${toolname} unused "handle_leaks" "-handle_leaks_only")
endif ()

###########################################################################
# Dr. Memory Framework (DRMF)

# We decided against putting the extension inside frameworks/, so
# framework-shared code goes here.  Things that should be global we
# put into functions for invocation inside extension subdirs.

configure_file(framework/drmf.cmake.in
  ${framework_dir}/DrMemoryFrameworkConfig.cmake
  @ONLY)
foreach (ext drsyscall umbra drfuzz)
  file(APPEND ${framework_dir}/DrMemoryFrameworkConfig.cmake "
set(DynamoRIO_EXT_${ext}_INC \${drmf_cwd}/include)
")
endforeach (ext)
configure_file(framework/drmf_version.cmake.in
  ${framework_dir}/DrMemoryFrameworkConfigVersion.cmake
  @ONLY)

# Export extensions for importing by clients.
if (X64)
  set(exported_targets_name "DRMFTarget64")
else (X64)
  set(exported_targets_name "DRMFTarget32")
endif (X64)

# DRi#948: we need to map Release and RelMinSize to RelWithDebInfo
file(WRITE ${framework_dir}/${exported_targets_name}.cmake "")
set(exported_targets_append "")
macro(export_target)
  if ("${CMAKE_VERSION}" VERSION_EQUAL "3.0" OR
      "${CMAKE_VERSION}" VERSION_GREATER "3.0")
    set(tgt_args "")
  else ()
    # We use a prefix primarily to make it easy to test the imported targets,
    # and to give a better "bundled extensions" feel.
    set(tgt_args NAMESPACE drmf_)
  endif ()
  export(TARGETS ${ARGV} ${tgt_args} FILE ${framework_dir}/${exported_targets_name}.cmake
    APPEND)
  set(toadd "
SET_PROPERTY(TARGET ${ARGV0} PROPERTY MAP_IMPORTED_CONFIG_RELEASE RelWithDebInfo)
SET_PROPERTY(TARGET ${ARGV0} PROPERTY MAP_IMPORTED_CONFIG_RELMINSIZE RelWithDebInfo)
")
  if (NOT DEBUG_BUILD)
    file(APPEND ${framework_dir}/${exported_targets_name}.cmake ${toadd})
  endif (NOT DEBUG_BUILD)
  # For install we want both debug and release:
  set(exported_targets_append "${exported_targets_append}${toadd}")
  # Hack to avoid "no parent scope" complaint.
  if (NOT "${ARGV}" MATCHES "drmemory_annotations")
    set(exported_targets_append "${exported_targets_append}" PARENT_SCOPE)
  endif ()
endmacro(export_target)

if (DR_ANNOTATIONS_SUPPORTED)
  add_library(drmemory_annotations STATIC drmemory/annotations_public.c)
  # TODO i#2266: Provide a CMake function like use_DynamoRIO_annotations.
  # We would both use it here and export it.
  # For now we make a library, which is actually a little easier than the
  # user having to compile our source file with special flags.
  # So the CMake function would just set up the include path and link to the lib.
  _DR_append_property_string(SOURCE drmemory/annotations_public.c
    COMPILE_FLAGS "${annot_cflags}")
  export_target(drmemory_annotations)
  install(TARGETS drmemory_annotations EXPORT ${exported_targets_name}
    DESTINATION ${DRMF_INSTALL_BIN})
endif ()

# Included prior to add_subdirectory() for building version.c which
# includes utils.h which includes drsyscall.h.
include_directories(drsyscall)

add_subdirectory(drsyscall)

# XXX: change this and include_directories above to
# find_package() + use_DynamoRIO_extension(drsyscall)
target_link_libraries(${client_target} drsyscall_int)

if (USE_DRSYMS)
  add_subdirectory(drsymcache)
  include_directories(drsymcache)
  target_link_libraries(${client_target} drsymcache_int)
endif (USE_DRSYMS)

# Add Umbra
add_subdirectory(umbra)

# XXX: change this to find_package() + use_DynamoRIO_extension(umbra)
include_directories(umbra)
target_link_libraries(${client_target} umbra_int)

# Add drfuzz
add_subdirectory(drfuzz)

# XXX: change this to find_package() + use_DynamoRIO_extension(drfuzz)
include_directories(drfuzz)
if (TOOL_DR_MEMORY)
  target_link_libraries(${client_target} drfuzz_int)
endif (TOOL_DR_MEMORY)

if (BUILD_TOOL_TESTS)
  add_subdirectory(tests/framework)

  # Test using our exported config.
  if (DEBUG)
    set(debug_cfg -DCMAKE_BUILD_TYPE=Debug)
  else ()
    set(debug_cfg -DCMAKE_BUILD_TYPE=RelWithDebInfo)
  endif ()
  set(drmf_bindir "${PROJECT_BINARY_DIR}/framework/samples")
  file(COPY "${PROJECT_SOURCE_DIR}/framework/samples/strace.c"
    DESTINATION "${drmf_bindir}")
  add_test(drmf_proj ${CMAKE_CTEST_COMMAND}
    --build-and-test "${drmf_bindir}"
    "${PROJECT_BINARY_DIR}/tests/drmf_proj"
    --build-generator ${CMAKE_GENERATOR}
    --build-project DRMF_samples # Needed for VS generators.
    --build-makeprogram ${CMAKE_MAKE_PROGRAM}
    --build-options ${debug_cfg} -DDrMemoryFramework_DIR:PATH=${framework_dir}
    -DDynamoRIO_DIR:PATH=${DynamoRIO_DIR})
  if (UNIX AND X86 AND NOT X64)
    set_tests_properties(drmf_proj PROPERTIES ENVIRONMENT
      "CFLAGS=-m32;CXXFLAGS=-m32")
  endif ()

  if (TOOL_DR_MEMORY)
    # unit tests
    add_executable(unit_tests ${srcs})
    _DR_append_property_list(TARGET unit_tests COMPILE_DEFINITIONS
      "${DEFINES_NO_D};BUILD_UNIT_TESTS;RC_IS_UNITTESTS")
    if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
      # ensure race-free parallel builds
      add_dependencies(unit_tests ${asm_utils_tgt})
    endif ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
    if (DEFINED DynamoRIO_RPATH)
      set(old_rpath ${DynamoRIO_RPATH})
    else ()
      set(old_rpath OFF)
    endif ()
    set(DynamoRIO_RPATH ON)
    configure_DynamoRIO_standalone(unit_tests)
    use_DynamoRIO_extension(unit_tests drmgr_static)
    use_DynamoRIO_extension(unit_tests drcontainers)
    use_DynamoRIO_extension(unit_tests drmgr_static)
    use_DynamoRIO_extension(unit_tests drx_static)
    use_DynamoRIO_extension(unit_tests drutil_static)
    use_DynamoRIO_extension(unit_tests drwrap_static)
    use_DynamoRIO_extension(unit_tests drsyms_static)
    use_DynamoRIO_extension(unit_tests drcovlib_static)
    target_link_libraries(unit_tests drsyscall_int)
    target_link_libraries(unit_tests umbra_int)
    target_link_libraries(unit_tests drsymcache_int)
    target_link_libraries(unit_tests drfuzz_int)
    set(DynamoRIO_RPATH ${old_rpath})
    get_target_path_for_execution(unit_relpath unit_tests)
    prefix_cmd_if_necessary(unit_relpath OFF ${unit_relpath})
    if (NOT ANDROID) # FIXME i#: not working on Android
      add_test(unit_tests ${unit_relpath})
    endif ()
    copy_target_to_device(unit_tests)
  endif (TOOL_DR_MEMORY)

endif (BUILD_TOOL_TESTS)

# XXX i#1497, i#1498: the drstrace front-end now uses drfrontendlib
# but there's a bunch of work getting the client to be cross-platform.
if (TOOL_DR_MEMORY AND WIN32)
  # We only build for Dr. Memory b/c we'd need extra work to deal w/
  # drheapstat's bin/bin32 different subdir.
  add_subdirectory(drstrace)
endif (TOOL_DR_MEMORY AND WIN32)

# FIXME i#1987: enable support of drltrace under MacOS
if (NOT APPLE)
  add_subdirectory(drltrace)
endif()

add_subdirectory(framework/samples)

###########################################################################
# installation

if (install_override)
  if (X64)
    set(EXP_DIR exports64)
  else (X64)
    set(EXP_DIR exports32)
  endif (X64)
  set(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/../${EXP_DIR}"
    CACHE PATH "install path" FORCE)
endif (install_override)

install(TARGETS ${client_target}
  RUNTIME DESTINATION "${INSTALL_LIB}" # dll
  LIBRARY DESTINATION "${INSTALL_LIB}" # .so
  PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
  WORLD_READ WORLD_EXECUTE)
install(TARGETS symquery DESTINATION "${INSTALL_BIN}"
  PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
  WORLD_READ WORLD_EXECUTE)
if (WIN32)
  # XXX i#926: remove winsyms once we remove postleaks.pl.
  # Also removed its pdb below via: PATTERN "winsyms.pdb" EXCLUDE
  install(TARGETS winsyms DESTINATION "${INSTALL_BIN}"
    PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE)
  install(FILES
    ${PROJECT_BINARY_DIR}/${BUILD_BIN}/dbghelp.dll
    ${PROJECT_BINARY_DIR}/${BUILD_BIN}/symsrv.dll
    DESTINATION "${INSTALL_BIN}"
    PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE)
  install(FILES
    ${PROJECT_BINARY_DIR}/${BUILD_BIN}/symsrv.yes
    DESTINATION "${INSTALL_BIN}")
  if (USE_DRSYMS)
    install(FILES
      ${PROJECT_BINARY_DIR}/${BUILD_BIN}/dbghelp.dll
      DESTINATION "${INSTALL_LIB}"
      PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
      WORLD_READ WORLD_EXECUTE)
  endif (USE_DRSYMS)
  if (USE_DRSYMS)
    install(FILES
      ${PROJECT_BINARY_DIR}/${BUILD_BIN}/drconfiglib.dll
      ${PROJECT_BINARY_DIR}/${BUILD_BIN}/drinjectlib.dll
      # frontend now imports from DR (i#885)
      ${PROJECT_BINARY_DIR}/${BUILD_BIN}/dynamorio.dll
      ${PROJECT_BINARY_DIR}/${BUILD_BIN}/drconfig.exe
      DESTINATION "${INSTALL_BIN}"
      PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
      WORLD_READ WORLD_EXECUTE)
  endif (USE_DRSYMS)
  # We want carriage returns for nice viewing in Notepad, etc.:
  file(READ ${CMAKE_CURRENT_SOURCE_DIR}/README readme_content)
  string(REPLACE "\n" "\r\n" readme_content ${readme_content})
  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/README.txt ${readme_content})
   install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/README.txt
    DESTINATION "${INSTALL_PREFIX}.")
else (WIN32)
  install(FILES
    ${CMAKE_CURRENT_SOURCE_DIR}/README
    DESTINATION "${INSTALL_PREFIX}.")
endif (WIN32)
install(FILES
  ${CMAKE_CURRENT_SOURCE_DIR}/license.txt
  DESTINATION "${INSTALL_PREFIX}.")
if (USE_DRSYMS)
  install(TARGETS ${toolname} DESTINATION "${INSTALL_BIN}"
    PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE)
else (USE_DRSYMS)
  install(FILES
    ${script_main}
    DESTINATION "${INSTALL_BIN}"
    PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE)
endif (USE_DRSYMS)
# We put "helper" files inside ${BIN_ARCH}/ so only top-level script is visible
if (NOT "${script_aux}" STREQUAL "")
  install(FILES
    ${script_aux}
    DESTINATION "${INSTALL_BIN}"
    PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE)
endif ()
if (ANDROID AND TOOL_DR_MEMORY)
  install(FILES
    "${PROJECT_BINARY_DIR}/${BUILD_BIN}/${toolname}"
    DESTINATION "${INSTALL_BIN}"
    PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE)
endif ()

if (TOOL_DR_MEMORY)
  install(FILES
    ${PROJECT_BINARY_DIR}/${BUILD_BIN}/suppress-default.txt
    DESTINATION "${INSTALL_BIN}"
    PERMISSIONS ${owner_access} GROUP_READ WORLD_READ)
  if (UNIX)
    install(FILES
      ${PROJECT_BINARY_DIR}/${BUILD_BIN}/valgrind2drmemory.pl
      DESTINATION "${INSTALL_PREFIX}bin"
      PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
      WORLD_READ WORLD_EXECUTE)
  endif (UNIX)
endif (TOOL_DR_MEMORY)

set(install_pdb_exclude
  # We don't want unit test pdb's, or internal tool pdb's.
  # XXX: it would be better to exclude these in the subdirs that own them,
  # which will be much easier once cmake supports auto-installing pdb's.
  PATTERN verinfo.pdb EXCLUDE
  PATTERN unit_tests.pdb EXCLUDE
  # symfetch is a fake dll so don't imply it's more by supplying a pdb.
  PATTERN symfetch.pdb EXCLUDE
  PATTERN drstrace_unit_tests.pdb EXCLUDE)
if (X64)
  install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${INSTALL_BIN}/README.txt\" \"Dr. Memory has preliminary 64-bit support that does not yet include detecting uninitialized reads.  The drstrace tool and the Dr. Syscall and Umbra libraries are fully supported for 64-bit.\n\")")
endif (X64)
install(DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/
  DESTINATION "${INSTALL_LIB}"
  FILE_PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
  WORLD_READ WORLD_EXECUTE
  FILES_MATCHING
  PATTERN "*.debug"
  PATTERN "*.pdb"
  REGEX ".*.dSYM/.*DWARF/.*" # too painful to get right # of backslash for literal .
  ${install_pdb_exclude}
  )

install(DIRECTORY ${PROJECT_BINARY_DIR}/${BUILD_BIN}/
  DESTINATION "${INSTALL_BIN}"
  FILE_PERMISSIONS ${owner_access} OWNER_EXECUTE
  GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
  FILES_MATCHING
  PATTERN "*.debug"
  PATTERN "*.pdb"
  REGEX ".*.dSYM/.*DWARF/.*" # too painful to get right # of backslash for literal .
  ${install_pdb_exclude}
  )

# create empty logs dir for release package
# be sure to escape ",$ since evaluated at install time not configure time
install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/${INSTALL_PREFIX}${toolname}/logs\")")
# CPack seems to ignore empty dirs so add a README file
install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${INSTALL_PREFIX}${toolname}/logs/README.txt\" \"Default destination for log files.\n\")")
# ditto for pcaches
install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/${INSTALL_PREFIX}${toolname}/logs/codecache\")")
install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${INSTALL_PREFIX}${toolname}/logs/codecache/README.txt\" \"Default destination for code cache files.\n\")")
# ditto for DR logdir
install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/${INSTALL_PREFIX}${toolname}/logs/dynamorio\")")
install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${INSTALL_PREFIX}${toolname}/logs/codecache/README.txt\" \"Default destination for DynamoRIO log files (for diagnostics only).\n\")")

if (UNIX)
  if (APPLE)
    set(LIB_SFX ".dylib")
  else (APPLE)
    set(LIB_SFX ".so")
  endif (APPLE)
endif (UNIX)

if (BUILDING_SUB_PACKAGE)
  # We're being included in another package (DR's).
  # Our rpaths assumed we had a dynamorio/ subdir, but now that's the upper dir.
  # Easier to add a symlink than to change rpaths.
  # XXX i#1855: this symlink makes "adb push" of our tarball's contents not work,
  # but presumably most users will untar on the Android device.
  # Fixing would require changing the script and the frontend binary.
  install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink .. dynamorio WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${INSTALL_PREFIX})")

  # Add a docs link in the top-level docs/ dir
  install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/docs/DrMemory.html\" \"<html>\n<head>\n<meta http-equiv=\\\"refresh\\\" content=\\\"0; URL=../drmemory/drmemory/docs/html/index.html\\\">\n</head><body></body>\")")

else (BUILDING_SUB_PACKAGE)
  # Include DR so user doesn't have to download separately (PR 457417).
  # We don't need docs/ or include/.
  install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}\")")

  if (UNIX)
    # CPACK_INSTALLED_DIRECTORIES since 2.8.3 now properly preserves symlinks
    # (http://www.itk.org/Bug/view.php?id=10096) so we can just copy the dir.
    install(DIRECTORY ${DynamoRIO_DIR}/../${LIB_ARCH}/${build_type}
      DESTINATION ${INSTALL_PREFIX}${DR_install_dir}/${LIB_ARCH}
      FILE_PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
      WORLD_READ WORLD_EXECUTE
      PATTERN "libdrdecode*" EXCLUDE
      # Static DR is large and we do not need it.
      PATTERN "libdynamorio_static*" EXCLUDE)
    # ext dir is currently empty but we need it to avoid drrun error (i#427)
    install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/ext/${LIB_ARCH}/${build_type}\")")
    # CPack seems to ignore empty dirs so add a README file
    install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/ext/${LIB_ARCH}/${build_type}/README.txt\" \"Currently empty.\n\")")
  endif (UNIX)

  # can't find way to package external individual files (can do whole dir)
  # and we don't need entire DR docs/, include/, etc. so we do these
  # files individually
  if (EXISTS "${DynamoRIO_SOURCE_DIR}/License.txt")
    set(DR_LICENSE_DIR "${DynamoRIO_SOURCE_DIR}")
  else ()
    set(DR_LICENSE_DIR "${DynamoRIO_DIR}/..")
  endif ()
  install(CODE "configure_file(\"${DR_LICENSE_DIR}/License.txt\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/License.txt\" COPYONLY)")
  install(CODE "configure_file(\"${DR_LICENSE_DIR}/ACKNOWLEDGEMENTS\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/ACKNOWLEDGEMENTS\" COPYONLY)")
  # rather whan whole README, which talks about running samples, etc.:
  install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/README.txt\" \"This is a copy of the parts of DynamoRIO needed to run ${toolname_cap_spc}.  See http://dynamorio.org for more information.\n\")")
  # on Windows we don't need all the ${BIN_ARCH}/ tools, and they take up space
  install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}\")")
  if (UNIX)
    if (USER_SPECIFIED_DynamoRIO_DIR) # else DR installs straight there (i#1449)
      install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/drrun\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/drrun\" COPYONLY)")
    endif ()
    if (NOT APPLE) # FIXME DRi#1286: no nudge support yet on MacOS
      install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/nudgeunix\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/nudgeunix\" COPYONLY)")
    endif (NOT APPLE)
    # drconfiglib and drinjectlib are now static libs so no need to install
  else (UNIX)
    if (TOOL_DR_HEAPSTAT OR NOT USE_DRSYMS)
      install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/drconfig.exe\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/drconfig.exe\" COPYONLY)")
      install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/drinject.exe\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/drinject.exe\" COPYONLY)")
      install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/drrun.exe\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/drrun.exe\" COPYONLY)")
    endif (TOOL_DR_HEAPSTAT OR NOT USE_DRSYMS)
    install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/drconfiglib.dll\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/drconfiglib.dll\" COPYONLY)")
    install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/drinjectlib.dll\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/drinjectlib.dll\" COPYONLY)")
    install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/DRcontrol.exe\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/DRcontrol.exe\" COPYONLY)")
    install(CODE "configure_file(\"${DynamoRIO_DIR}/../${BIN_ARCH}/DRview.exe\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/${BIN_ARCH}/DRview.exe\" COPYONLY)")
  endif (UNIX)

  if (TOOL_DR_MEMORY)
    # For the new -coverage feature, we include a copy of drcov2lcov.
    # We keep it inside the dynamorio/ subdir so its rpath will still work.
    # XXX: a debug build of drmem pointing at an external DR will pull in a
    # release-build drcov2lcov whose rpath will point at a release DR yet
    # we only copy debug DR so it will fail -- we just live with this though
    # as a full RC package will work fine.
    if (USER_SPECIFIED_DynamoRIO_DIR)
      set(covdir tools)
    else ()
      set(covdir clients)
    endif ()
    if (UNIX)
      # We can't use get_target_property() for USER_SPECIFIED_DynamoRIO_DIR
      set(covhelper drcov2lcov)
    else ()
      set(covhelper drcov2lcov.exe)
    endif ()
    install(CODE "configure_file(\"${DynamoRIO_DIR}/../${covdir}/${BIN_ARCH}/${covhelper}\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/tools/${BIN_ARCH}/${covhelper}\" COPYONLY)")
    if (WIN32)
      install(CODE "configure_file(\"${DynamoRIO_DIR}/../${LIB_ARCH}/${build_type}/dynamorio.dll\" \"\${CMAKE_INSTALL_PREFIX}/${DR_install_dir}/tools/${BIN_ARCH}/dynamorio.dll\" COPYONLY)")
    endif ()
  endif ()
endif (BUILDING_SUB_PACKAGE)

# DRMF install rules
install(FILES
  ${framework_incdir}/drmemory_framework.h
  DESTINATION ${DRMF_INSTALL_INC})
if (DR_ANNOTATIONS_SUPPORTED)
  install(FILES
    ${framework_incdir}/drmemory_annotations.h
    ${framework_incdir}/dr_annotations.h
    ${framework_incdir}/dr_annotations_asm.h
    DESTINATION ${DRMF_INSTALL_INC})
endif ()
install(FILES ${framework_dir}/DrMemoryFrameworkConfigVersion.cmake
  ${framework_dir}/DrMemoryFrameworkConfig.cmake
  DESTINATION ${DRMF_INSTALL})
# These cover all subdirs
install(DIRECTORY ${framework_bindir}/
  DESTINATION ${DRMF_INSTALL_BIN}
  FILE_PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
  WORLD_READ WORLD_EXECUTE
  FILES_MATCHING
  PATTERN "*.debug"
  PATTERN "*.pdb"
  REGEX ".*.dSYM/.*DWARF/.*" # too painful to get right # of backslash for literal .
  PATTERN "*_int.pdb" EXCLUDE
  )

# We must append to this file to avoid cmake_install.cmake's diff from thinking
# the exports have changed and thus clobbering the other config's files.
install(CODE "file(APPEND \"${PROJECT_BINARY_DIR}/CMakeFiles/Export/cmake/${exported_targets_name}.cmake\" \"${exported_targets_append}\")")
# Create the exported targets file
# Note that we do not use the drmf_ NAMESPACE here: it's only needed for
# internal tests.
install(EXPORT ${exported_targets_name} DESTINATION ${DRMF_INSTALL})

# Check that we have version resources in all our binaries.
# We do this at install time once we have all binaries built, as it's a pain
# to individually add post-build commands.
if (WIN32)
  install(CODE "include(\"${PROJECT_SOURCE_DIR}/make/rccheck.cmake\")")
  # We'd need cmake 3.14+ and CMP0087=NEW for a generator expression to work
  # directly in install(CODE).  We instead have to make a file.
  set(verinfo_locator "verinfo_locator.cmake")
  file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${verinfo_locator}" CONTENT
"set(VERINFO \"$<TARGET_FILE:verinfo>\")\n")
  install(CODE "include(\"${CMAKE_CURRENT_BINARY_DIR}/${verinfo_locator}\")")
  install(CODE "check_version_resources(\"${PROJECT_BINARY_DIR}\" \"\${VERINFO}\")")
endif ()

###########################################################################
# packaging

# "make package package_source"

if (USER_SPECIFIED_DynamoRIO_DIR)
  set(favicon_path "${DynamoRIO_DIR}/../docs/html/favicon.ico")
else (USER_SPECIFIED_DynamoRIO_DIR)
  set(favicon_path "${DynamoRIO_SOURCE_DIR}/api/docs/images/favicon.ico")
endif (USER_SPECIFIED_DynamoRIO_DIR)

if (UNIX)
  # not bothering with TZ or TBZ2 or STGZ (.sh)
  set(CPACK_GENERATOR "TGZ")
  set(CPACK_SOURCE_GENERATOR "TGZ")
  # We've already split out our separate .debug files and stripped the
  # originals in our build rules
  set(CPACK_STRIP_FILES OFF)
  if (VMKERNEL)
    set(CPACK_SYSTEM_NAME "ESXi")
  else (VMKERNEL)
    if (APPLE)
      set(CPACK_SYSTEM_NAME "MacOS")
    else (APPLE)
      set(CPACK_SYSTEM_NAME "Linux")
    endif (APPLE)
  endif (VMKERNEL)
else (UNIX)
  # We don't require NSIS or WIX since we do "make package" as part of test suite
  set(WIX "WIX-NOTFOUND")
  set(NSIS "NSIS-NOTFOUND")
  # Our WIX config requires recent features
  if ("${CMAKE_VERSION}" VERSION_EQUAL "3.3" OR
      "${CMAKE_VERSION}" VERSION_GREATER "3.3")
    # WIX's candle.exe must be on the path for cpack to run it, so hints don't help
    find_program(WIX candle DOC "WIX for creating installer")
  endif ()
  # Prefer WIX
  if (NOT WIX)
    find_program(NSIS nsis HINT "$ENV{PROGRAMFILES}/NSIS" DOC
      "NSIS for creating installer")
  endif ()
  if (NSIS)
    message(STATUS "NSIS found: will build NSIS-based installer")
    set(CPACK_GENERATOR "ZIP;NSIS")
    # Ensure we have the NSIS with an 8192 PATH limit (i#1029)
    get_filename_component(nsis_path "${NSIS}" PATH)
    if (NOT EXISTS "${nsis_path}/makensis.exe")
      message(FATAL_ERROR "NSIS check needs to be updated")
    endif ()
    # we may have already run find_program on strings up above
    if (NOT strings)
      find_program(strings strings)
    endif ()
    if (strings)
      execute_process(COMMAND ${strings} "${nsis_path}/makensis.exe"
        RESULT_VARIABLE strings_result
        ERROR_QUIET
        OUTPUT_VARIABLE strings_out)
      if (strings_result)
        message(FATAL_ERROR "*** ${strings} failed to run ***\n")
      endif (strings_result)
      # The default has "NSIS_MAX_STRLEN\n1024": make sure we have 8192.
      string(REGEX MATCH "NSIS_MAX_STRLEN\n8192" big_limit "${strings_out}")
      if (big_limit)
        message(STATUS "NSIS has larger 8192 path limit")
      else ()
        message(FATAL_ERROR "Installed NSIS has a too-small path limit.  See i#1029.")
      endif ()
    endif (strings)
  elseif (WIX)
    message(STATUS "WIX found: will build WIX-based .msi installer")
    # Make sure to specify BUILDING_PACKAGE. See i#1099 about absolute path.
    set(CPACK_GENERATOR "ZIP;WIX")
    # Variables below are set to allow for upgrade but prevent
    # reinstalling the same version. See i#1620.
    set(CPACK_WIX_UPGRADE_GUID 1A8D7CEE-2ABA-4462-B0D6-86F26715128E)
    set(CPACK_WIX_PRODUCT_GUID 67675AD6-1FB0-4DE1-9ECF-84997515025E)
    set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/make/WIX.patches.in")
    set(CPACK_WIX_EXTRA_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/make/WIX.extra.in")
    # This is required for the util:InternetShortcut in WIX.extra.in
    set(CPACK_WIX_EXTENSIONS "WiXUtilExtension")
    # This should show up in Add/Remove Programs:
    set(CPACK_WIX_PROPERTY_ARPURLINFOABOUT "http://drmemory.org")
    set(CPACK_WIX_PRODUCT_ICON "${favicon_path}")
  else ()
    message(STATUS "Neither NSIS nor WIX found (please install and add to path if desired).  Creating only a zip package.")
    set(CPACK_GENERATOR "ZIP")
  endif ()
  set(CPACK_SOURCE_GENERATOR "ZIP")
  set(CPACK_SYSTEM_NAME "Windows")
  if (USE_DRSYMS)
    set(CPACK_SYSTEM_NAME "Windows")
  else (USE_DRSYMS)
    set(CPACK_SYSTEM_NAME "Cygwin")
  endif (USE_DRSYMS)
endif (UNIX)

set(CPACK_PACKAGE_NAME "${toolname_cap_spc}")
set(CPACK_PACKAGE_VENDOR "Google")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${toolname_cap_spc} Memory Debugging Tool")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/license.txt")
set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README")

set(CPACK_SOURCE_IGNORE_FILES "~$" "/.svn" "/.git")

set(CPACK_PACKAGE_VERSION "${TOOL_VERSION_NUMBER}")
string(REGEX REPLACE
  "^([0-9]+)\\..*" "\\1" CPACK_PACKAGE_VERSION_MAJOR "${TOOL_VERSION_NUMBER}")
string(REGEX REPLACE
  "^[0-9]+\\.([0-9]+)\\..*" "\\1" CPACK_PACKAGE_VERSION_MINOR "${TOOL_VERSION_NUMBER}")
string(REGEX REPLACE
  "^[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" CPACK_PACKAGE_VERSION_PATCH "${TOOL_VERSION_NUMBER}")

if (WIN32 AND NOT BUILDING_SUB_PACKAGE)
  # Include DR so user doesn't have to download separately (PR 457417).
  # For Unix we do this file-by-file above b/c here we'd get duplicate
  # files due to cpack not preserving symlinks (filed as
  # http://www.itk.org/Bug/view.php?id=10096)
  # We don't need docs/, include/, or 64-bit.
  # We do include the debug lib (and default DR log dir) for
  # debugging in the field: it does take space but we'd probably regret
  # not supplying it.
  if (NOT USER_SPECIFIED_DynamoRIO_DIR)
    # install to drmemory exports dir
    install(DIRECTORY "${DynamoRIO_DIR}/../${LIB_ARCH}"
      DESTINATION "${DR_install_dir}"
      # Static DR is large and we do not need it.
      PATTERN "dynamorio_static*" EXCLUDE)
    # we don't need ext/ at all b/c we use static libs
  else (NOT USER_SPECIFIED_DynamoRIO_DIR)
    # don't take time and space copying: user can pass same dr to frontend,
    # so we only copy for package target
    if (DEBUG_BUILD)
      set(CPACK_INSTALLED_DIRECTORIES
        "${DynamoRIO_DIR}/../${LIB_ARCH};${DR_install_dir}/${LIB_ARCH}"
        "${DynamoRIO_DIR}/../ext/${LIB_ARCH};${DR_install_dir}/ext/${LIB_ARCH}")
    else (DEBUG_BUILD)
      set(CPACK_INSTALLED_DIRECTORIES
        "${DynamoRIO_DIR}/../${LIB_ARCH}/release;${DR_install_dir}/${LIB_ARCH}/release"
        "${DynamoRIO_DIR}/../ext/${LIB_ARCH}/release;${DR_install_dir}/ext/${LIB_ARCH}/release")
    endif (DEBUG_BUILD)
  endif (NOT USER_SPECIFIED_DynamoRIO_DIR)
endif (WIN32 AND NOT BUILDING_SUB_PACKAGE)

# CPack tarballs do not allow setting a different name for the base
# directory and the file: I tried a ton of CPack variables for "install
# dir" and looked at the source code.  Most of the variables are for the
# other installers (rpm, nsis).  I can hack it via
# CPACK_TEMPORARY_PACKAGE_FILE_NAME if I hardcode the exentsion: but maybe
# having the full version in the base dir is a good thing, though I'm not
# sure about the caps.
# We omit the -NN suffix for the build number if it is zero.
if ("${TOOL_BUILD_NUMBER}" STREQUAL "0")
  set(PACKAGE_SUFFIX "")
else ()
  set(PACKAGE_SUFFIX "-${TOOL_BUILD_NUMBER}")
endif ()
set(CPACK_PACKAGE_FILE_NAME
  "${toolname_cap}-${CPACK_SYSTEM_NAME}-${CPACK_PACKAGE_VERSION}${PACKAGE_SUFFIX}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME
  "${toolname_cap}-${CPACK_PACKAGE_VERSION}-${TOOL_BUILD_NUMBER}-Source")

# NSIS settings
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${toolname_cap_spc}")
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${toolname_cap_spc}")
set(CPACK_PACKAGE_RELOCATABLE "true")
set(CPACK_NSIS_MODIFY_PATH ON)
if (PERL_TO_EXE OR WIN32 AND USE_DRSYMS AND TOOL_DR_MEMORY)
  # CMake hardcodes bin/ for desktop links (CMake bug #7828) so
  # we rearranged our dirs since we really need one.
  set(CPACK_PACKAGE_EXECUTABLES "${toolname}" "${toolname_cap_spc} (drag your app here)")
  set(CPACK_CREATE_DESKTOP_LINKS "${toolname}")
endif (PERL_TO_EXE OR WIN32 AND USE_DRSYMS AND TOOL_DR_MEMORY)
set(CPACK_NSIS_MENU_LINKS
  # We automatically get an entry for ${toolname}.exe since it's in the
  # CPACK_PACKAGE_EXECUTABLES list, even though we don't want one.
  "bin/" "Explore ${toolname_cap_spc} (drag your app onto ${toolname}.exe)"
  "${toolname}/docs/html/index.html" "${toolname_cap_spc} documentation"
  "http://drmemory.org/" "${toolname_cap_spc} web page")
set(CPACK_NSIS_MUI_ICON "${favicon_path}")
# XXX: CPACK_PACKAGE_ICON: need a .bmp

# i#1009c#4: auto-register Dr. Memory as a Visual Studio External Tool
if (WIN32)
  install(TARGETS vs_external_tool DESTINATION "${INSTALL_BIN}"
    PERMISSIONS ${owner_access}
    OWNER_EXECUTE GROUP_READ GROUP_EXECUTE  WORLD_READ WORLD_EXECUTE)
  set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait  '\\\"$INSTDIR\\\\bin\\\\vs_external_tool.exe\\\" \\\"$INSTDIR\\\\bin\\\\drmemory.exe'")
  set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "ExecWait  '\\\"$INSTDIR\\\\bin\\\\vs_external_tool.exe\\\" -uninstall'")
endif (WIN32)

# Let external build file override all settings, but before CPack reads them
set(AUX_MAKEFILE "" CACHE FILEPATH "Path to an auxiliary CMakeLists.txt file.")
if (AUX_MAKEFILE)
  include("${AUX_MAKEFILE}")
endif (AUX_MAKEFILE)

include(CPack)
